1 // -*- c++ -*- ////////////////////////////////////////////////////////////////
2 // Name: unix/dialup.cpp
3 // Purpose: Network related wxWindows classes and functions
4 // Author: Karsten Ballüder
8 // Copyright: (c) Karsten Ballüder
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
15 # pragma implementation "dialup.h"
18 #if wxUSE_DIALUP_MANAGER
24 #include "wx/string.h"
26 #include "wx/dialup.h"
28 #include "wx/filefn.h"
32 #include "wx/process.h"
35 #include "wx/wxchar.h"
42 #define __STRICT_ANSI__
43 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
49 // ----------------------------------------------------------------------------
50 // A class which groups functions dealing with connecting to the network from a
51 // workstation using dial-up access to the net. There is at most one instance
52 // of this class in the program accessed via GetDialUpManager().
53 // ----------------------------------------------------------------------------
57 * 1. more configurability for Unix: i.e. how to initiate the connection, how
58 * to check for online status, &c.
59 * 2. add a "long Dial(long connectionId = -1)" function which asks the user
60 * about which connection to dial (this may be done using native dialogs
61 * under NT, need generic dialogs for all others) and returns the identifier
62 * of the selected connection (it's opaque to the application) - it may be
63 * reused later to dial the same connection later (or use strings instead of
65 * 3. add an async version of dialing functions which notify the caller about
66 * the progress (or may be even start another thread to monitor it)
67 * 4. the static creation/accessor functions are not MT-safe - but is this
68 * really crucial? I think we may suppose they're always called from the
72 class WXDLLEXPORT wxDialUpManagerImpl
: public wxDialUpManager
75 wxDialUpManagerImpl();
76 ~wxDialUpManagerImpl();
78 /** Could the dialup manager be initialized correctly? If this function
79 returns FALSE, no other functions will work neither, so it's a good idea
80 to call this function and check its result before calling any other
81 wxDialUpManager methods.
83 virtual bool IsOk() const
86 /** The simplest way to initiate a dial up: this function dials the given
87 ISP (exact meaning of the parameter depends on the platform), returns
88 TRUE on success or FALSE on failure and logs the appropriate error
89 message in the latter case.
90 @param nameOfISP optional paramater for dial program
91 @param username unused
92 @param password unused
94 virtual bool Dial(const wxString
& nameOfISP
,
95 const wxString
& WXUNUSED(username
),
96 const wxString
& WXUNUSED(password
),
99 // Hang up the currently active dial up connection.
100 virtual bool HangUp();
102 // returns TRUE if the computer is connected to the network: under Windows,
103 // this just means that a RAS connection exists, under Unix we check that
104 // the "well-known host" (as specified by SetWellKnownHost) is reachable
105 virtual bool IsOnline() const
108 return m_IsOnline
== Net_Connected
;
111 // do we have a constant net connection?
112 virtual bool IsAlwaysOnline() const;
114 // returns TRUE if (async) dialing is in progress
115 virtual bool IsDialing() const
116 { return m_DialProcess
!= NULL
; }
118 // cancel dialing the number initiated with Dial(async = TRUE)
119 // NB: this won't result in DISCONNECTED event being sent
120 virtual bool CancelDialing();
122 size_t GetISPNames(class wxArrayString
&) const
125 // sometimes the built-in logic for determining the online status may fail,
126 // so, in general, the user should be allowed to override it. This function
127 // allows to forcefully set the online status - whatever our internal
128 // algorithm may think about it.
129 virtual void SetOnlineStatus(bool isOnline
= TRUE
)
130 { m_IsOnline
= isOnline
? Net_Connected
: Net_No
; }
132 // set misc wxDialUpManager options
133 // --------------------------------
135 // enable automatical checks for the connection status and sending of
136 // wxEVT_DIALUP_CONNECTED/wxEVT_DIALUP_DISCONNECTED events. The interval
137 // parameter is only for Unix where we do the check manually: under
138 // Windows, the notification about the change of connection status is
141 // Returns FALSE if couldn't set up automatic check for online status.
142 virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds
);
144 // disable automatic check for connection status change - notice that the
145 // wxEVT_DIALUP_XXX events won't be sent any more neither.
146 virtual void DisableAutoCheckOnlineStatus();
148 // under Unix, the value of well-known host is used to check whether we're
149 // connected to the internet. It's unused under Windows, but this function
150 // is always safe to call. The default value is www.yahoo.com.
151 virtual void SetWellKnownHost(const wxString
& hostname
,
153 /** Sets the commands to start up the network and to hang up
154 again. Used by the Unix implementations only.
156 virtual void SetConnectCommand(const wxString
&command
, const wxString
&hupcmd
)
157 { m_ConnectCommand
= command
; m_HangUpCommand
= hupcmd
; }
159 //private: -- Sun CC 4.2 objects to using NetConnection enum as the return
160 // type if it is declared private
162 // the possible results of testing for Online() status
165 Net_Unknown
= -1, // we couldn't learn anything
166 Net_No
, // no network connection [currently]
167 Net_Connected
// currently connected
170 // the possible net connection types
173 NetDevice_None
= 0x0000, // no network devices (authoritative)
174 NetDevice_Unknown
= 0x0001, // test doesn't work on this OS
175 NetDevice_Modem
= 0x0002, // we have a modem
176 NetDevice_LAN
= 0x0004 // a network card
180 // the current status
181 NetConnection m_IsOnline
;
183 // the connection we have with the network card
184 NetConnection m_connCard
;
186 // Can we use ifconfig to list active devices?
187 int m_CanUseIfconfig
;
189 // The path to ifconfig
190 wxString m_IfconfigPath
;
192 // Can we use ping to find hosts?
194 // The path to ping program
198 wxString m_BeaconHost
;
199 // beacon host portnumber for connect:
202 // command to connect to network
203 wxString m_ConnectCommand
;
204 // command to hang up
205 wxString m_HangUpCommand
;
208 // a timer for regular testing
209 class AutoCheckTimer
*m_timer
;
210 friend class AutoCheckTimer
;
212 // a wxProcess for dialling in background
213 class wxDialProcess
*m_DialProcess
;
214 // pid of dial process
216 friend class wxDialProcess
;
219 void CheckStatus(bool fromAsync
= FALSE
) const;
222 void CheckStatusInternal();
224 // check /proc/net (Linux only) for ppp/eth interfaces, returns the bit
225 // mask of NetDeviceType constants
228 // check output of ifconfig command for PPP/SLIP/PLIP devices, returns the
229 // bit mask of NetDeviceType constants
232 // combines the 2 possible checks for determining the connection status
233 NetConnection
CheckConnectAndPing();
236 NetConnection
CheckPing();
238 // check by connecting to host on given port.
239 NetConnection
CheckConnect();
243 class AutoCheckTimer
: public wxTimer
246 AutoCheckTimer(wxDialUpManagerImpl
*dupman
)
252 virtual bool Start( int millisecs
= -1, bool WXUNUSED(one_shot
) = FALSE
)
253 { m_started
= TRUE
; return wxTimer::Start(millisecs
, FALSE
); }
255 virtual void Notify()
256 { wxLogTrace(wxT("Checking dial up network status.")); m_dupman
->CheckStatus(); }
259 { if ( m_started
) wxTimer::Stop(); }
262 wxDialUpManagerImpl
*m_dupman
;
265 class wxDialProcess
: public wxProcess
268 wxDialProcess(wxDialUpManagerImpl
*dupman
)
272 void Disconnect() { m_DupMan
= NULL
; }
273 virtual void OnTerminate(int WXUNUSED(pid
), int WXUNUSED(status
))
277 m_DupMan
->m_DialProcess
= NULL
;
278 m_DupMan
->CheckStatus(TRUE
);
282 wxDialUpManagerImpl
*m_DupMan
;
286 wxDialUpManagerImpl::wxDialUpManagerImpl()
289 m_connCard
= Net_Unknown
;
290 m_DialProcess
= NULL
;
292 m_CanUseIfconfig
= -1; // unknown
293 m_CanUsePing
= -1; // unknown
294 m_BeaconHost
= WXDIALUP_MANAGER_DEFAULT_BEACONHOST
;
298 m_ConnectCommand
= _T("/usr/etc/ppp");
299 #elif defined(__LINUX__)
300 // default values for Debian/GNU linux
301 m_ConnectCommand
= _T("pon");
302 m_HangUpCommand
= _T("poff");
305 wxChar
* dial
= wxGetenv(_T("WXDIALUP_DIALCMD"));
306 wxChar
* hup
= wxGetenv(_T("WXDIALUP_HUPCMD"));
307 SetConnectCommand(dial
? wxString(dial
) : m_ConnectCommand
,
308 hup
? wxString(hup
) : m_HangUpCommand
);
311 wxDialUpManagerImpl::~wxDialUpManagerImpl()
313 if(m_timer
) delete m_timer
;
316 m_DialProcess
->Disconnect();
317 m_DialProcess
->Detach();
322 wxDialUpManagerImpl::Dial(const wxString
&isp
,
323 const wxString
& WXUNUSED(username
),
324 const wxString
& WXUNUSED(password
),
327 if(m_IsOnline
== Net_Connected
)
331 if(m_ConnectCommand
.Find(wxT("%s")))
332 cmd
.Printf(m_ConnectCommand
,m_ISPname
.c_str());
334 cmd
= m_ConnectCommand
;
338 m_DialProcess
= new wxDialProcess(this);
339 m_DialPId
= (int)wxExecute(cmd
, FALSE
, m_DialProcess
);
342 delete m_DialProcess
;
343 m_DialProcess
= NULL
;
350 return wxExecute(cmd
, /* sync */ TRUE
) == 0;
353 bool wxDialUpManagerImpl::HangUp()
355 if(m_IsOnline
== Net_No
)
359 wxLogError(_("Already dialling ISP."));
363 if(m_HangUpCommand
.Find(wxT("%s")))
364 cmd
.Printf(m_HangUpCommand
,m_ISPname
.c_str(), m_DialProcess
);
366 cmd
= m_HangUpCommand
;
367 return wxExecute(cmd
, /* sync */ TRUE
) == 0;
371 bool wxDialUpManagerImpl::CancelDialing()
375 return kill(m_DialPId
, SIGTERM
) > 0;
378 bool wxDialUpManagerImpl::EnableAutoCheckOnlineStatus(size_t nSeconds
)
380 DisableAutoCheckOnlineStatus();
381 m_timer
= new AutoCheckTimer(this);
382 bool rc
= m_timer
->Start(nSeconds
*1000);
391 void wxDialUpManagerImpl::DisableAutoCheckOnlineStatus()
402 void wxDialUpManagerImpl::SetWellKnownHost(const wxString
& hostname
, int portno
)
404 if(hostname
.Length() == 0)
406 m_BeaconHost
= WXDIALUP_MANAGER_DEFAULT_BEACONHOST
;
411 // does hostname contain a port number?
412 wxString port
= hostname
.After(wxT(':'));
415 m_BeaconHost
= hostname
.Before(wxT(':'));
416 m_BeaconPort
= wxAtoi(port
);
420 m_BeaconHost
= hostname
;
421 m_BeaconPort
= portno
;
426 void wxDialUpManagerImpl::CheckStatus(bool fromAsync
) const
428 // This function calls the CheckStatusInternal() helper function
429 // which is OS - specific and then sends the events.
431 NetConnection oldIsOnline
= m_IsOnline
;
432 ( /* non-const */ (wxDialUpManagerImpl
*)this)->CheckStatusInternal();
434 // now send the events as appropriate: i.e. if the status changed and
435 // if we're in defined state
436 if(m_IsOnline
!= oldIsOnline
437 && m_IsOnline
!= Net_Unknown
438 && oldIsOnline
!= Net_Unknown
)
440 wxDialUpEvent
event(m_IsOnline
== Net_Connected
, ! fromAsync
);
441 (void)wxTheApp
->ProcessEvent(event
);
446 We first try to find out if ppp interface is active. If it is, we assume
447 that we're online but don't have a permanent connection (this is false if a
448 networked machine uses modem to connect to somewhere else, but we can't do
449 anything in this case anyhow).
451 If no ppp interface is detected, we check for eth interface. If it is
452 found, we check that we can, indeed, connect to an Internet host. The logic
453 here is that connection check should be fast enough in this case and we
454 don't want to give false positives in a (common) case of a machine on a LAN
455 which is not connected to the outside.
457 If we didn't find either ppp or eth interfaces, we stop here and decide
458 that we're connected. However, if couldn't check for this, we try to ping a
459 remote host just in case.
461 NB1: Checking for the interface presence can be done in 2 ways
462 a) reading /proc/net/dev under Linux
463 b) spawning ifconfig under any OS
465 The first method is faster but only works under Linux.
467 NB2: pinging, actually, means that we first try to connect "manually" to
468 a port on remove machine and if it fails, we run ping.
471 void wxDialUpManagerImpl::CheckStatusInternal()
473 m_IsOnline
= Net_Unknown
;
475 // first do quick checks to determine what kind of network devices do we
477 int netDeviceType
= CheckProcNet();
478 if ( netDeviceType
== NetDevice_Unknown
)
480 // nothing found, try ifconfig too
481 netDeviceType
= CheckIfconfig();
484 switch ( netDeviceType
)
487 // no network devices, no connection
492 // we still do ping to confirm that we're connected but we only do
493 // it once and hope that the purpose of the network card (i.e.
494 // whether it used for connecting to the Internet or just to a
495 // LAN) won't change during the program lifetime
496 if ( m_connCard
== Net_Unknown
)
498 m_connCard
= CheckConnectAndPing();
500 m_IsOnline
= m_connCard
;
503 case NetDevice_Unknown
:
504 // try to ping just in case
505 m_IsOnline
= CheckConnectAndPing();
508 case NetDevice_LAN
+ NetDevice_Modem
:
509 case NetDevice_Modem
:
510 // assume we're connected
511 m_IsOnline
= Net_Connected
;
515 wxFAIL_MSG(_T("Unexpected netDeviceType"));
519 bool wxDialUpManagerImpl::IsAlwaysOnline() const
521 wxDialUpManagerImpl
*self
= wxConstCast(this, wxDialUpManagerImpl
);
523 int netDeviceType
= self
->CheckProcNet();
524 if ( netDeviceType
== NetDevice_Unknown
)
526 // nothing found, try ifconfig too
527 netDeviceType
= self
->CheckIfconfig();
530 if ( netDeviceType
== NetDevice_Unknown
)
532 // this is the only thing we can do unfortunately...
538 // we are only permanently online if we have a network card
539 return (netDeviceType
& NetDevice_LAN
) != 0;
543 wxDialUpManagerImpl::NetConnection
wxDialUpManagerImpl::CheckConnectAndPing()
547 // first try connecting - faster
548 conn
= CheckConnect();
549 if ( conn
== Net_Unknown
)
558 wxDialUpManagerImpl::NetConnection
wxDialUpManagerImpl::CheckConnect()
560 // second method: try to connect to a well known host:
561 // This can be used under Win 9x, too!
563 struct sockaddr_in serv_addr
;
565 if((hp
= gethostbyname(m_BeaconHost
.mb_str())) == NULL
)
566 return Net_No
; // no DNS no net
568 serv_addr
.sin_family
= hp
->h_addrtype
;
569 memcpy(&serv_addr
.sin_addr
,hp
->h_addr
, hp
->h_length
);
570 serv_addr
.sin_port
= htons(m_BeaconPort
);
573 if( ( sockfd
= socket(hp
->h_addrtype
, SOCK_STREAM
, 0)) < 0)
575 return Net_Unknown
; // no info
578 if( connect(sockfd
, (struct sockaddr
*) &serv_addr
,
579 sizeof(serv_addr
)) >= 0)
582 return Net_Connected
; // we can connect, so we have a network!
584 else // failed to connect
586 if(errno
== ENETUNREACH
)
587 return Net_No
; // network is unreachable
589 return Net_Unknown
; // connect failed, but don't know why
595 wxDialUpManagerImpl::CheckProcNet()
597 // assume that the test doesn't work
598 int netDevice
= NetDevice_Unknown
;
601 if (wxFileExists(_T("/proc/net/route")))
603 // cannot use wxFile::Length because file doesn't support seeking, so
604 // use stdio directly
605 FILE *f
= fopen("/proc/net/route", "rt");
608 // now we know that we will find all devices we may have
609 netDevice
= NetDevice_None
;
613 while (fgets(output
, 256, f
) != NULL
)
615 if ( strstr(output
, "eth") ) // network card
617 netDevice
|= NetDevice_LAN
;
619 else if (strstr(output
,"ppp") // ppp
620 || strstr(output
,"sl") // slip
621 || strstr(output
,"pl")) // plip
623 netDevice
|= NetDevice_Modem
;
637 wxDialUpManagerImpl::CheckIfconfig()
639 // assume that the test doesn't work
640 int netDevice
= NetDevice_Unknown
;
642 // first time check for ifconfig location
643 if ( m_CanUseIfconfig
== -1 ) // unknown
645 static const wxChar
*ifconfigLocations
[] =
647 _T("/sbin"), // Linux, FreeBSD
648 _T("/usr/sbin"), // SunOS, Solaris, AIX, HP-UX
649 _T("/usr/etc"), // IRIX
652 for ( size_t n
= 0; n
< WXSIZEOF(ifconfigLocations
); n
++ )
654 wxString
path(ifconfigLocations
[n
]);
655 path
<< _T("/ifconfig");
657 if ( wxFileExists(path
) )
659 m_IfconfigPath
= path
;
665 if ( m_CanUseIfconfig
!= 0 ) // unknown or yes
667 wxLogNull ln
; // suppress all error messages
669 wxASSERT_MSG( m_IfconfigPath
.length(),
670 _T("can't use ifconfig if it wasn't found") );
672 wxString tmpfile
= wxGetTempFileName("_wxdialuptest");
673 wxString cmd
= "/bin/sh -c \'";
674 cmd
<< m_IfconfigPath
;
675 #if defined(__SOLARIS__) || defined (__SUNOS__)
676 // need to add -a flag
678 #elif defined(__LINUX__) || defined(__SGI__)
679 // nothing to be added to ifconfig
680 #elif defined(__FREEBSD__)
683 #elif defined(__HPUX__)
684 // VZ: a wild guess (but without it, ifconfig fails completely)
687 # pragma warning "No ifconfig information for this OS."
688 m_CanUseIfconfig
= 0;
691 cmd
<< " >" << tmpfile
<< '\'';
692 /* I tried to add an option to wxExecute() to not close stdout,
693 so we could let ifconfig write directly to the tmpfile, but
694 this does not work. That should be faster, as it doesn´t call
695 the shell first. I have no idea why. :-( (KB) */
696 if ( wxExecute(cmd
,TRUE
/* sync */) == 0 )
698 m_CanUseIfconfig
= 1;
700 if( file
.Open(tmpfile
) )
703 if ( file
.ReadAll(&output
) )
705 // FIXME shouldn't we grep for "^ppp"? (VZ)
707 bool hasModem
= FALSE
,
710 #if defined(__SOLARIS__) || defined (__SUNOS__)
711 // dialup device under SunOS/Solaris
712 hasModem
= strstr(output
,"ipdptp") != (char *)NULL
;
713 hasLAN
= strstr(output
, "hme") != (char *)NULL
;
714 #elif defined(__LINUX__) || defined (__FREEBSD__)
715 hasModem
= strstr(output
,"ppp") // ppp
716 || strstr(output
,"sl") // slip
717 || strstr(output
,"pl"); // plip
718 hasLAN
= strstr(output
, "eth") != NULL
;
719 #elif defined(__SGI__) // IRIX
720 hasModem
= strstr(output
, "ppp") != NULL
; // PPP
721 #elif defined(__HPUX__)
722 // if could run ifconfig on interface, then it exists
726 netDevice
= NetDevice_None
;
728 netDevice
|= NetDevice_Modem
;
730 netDevice
|= NetDevice_LAN
;
732 //else: error reading the file
734 //else: error opening the file
736 else // could not run ifconfig correctly
738 m_CanUseIfconfig
= 0; // don´t try again
741 (void) wxRemoveFile(tmpfile
);
747 wxDialUpManagerImpl::NetConnection
wxDialUpManagerImpl::CheckPing()
749 // First time check for ping location. We only use the variant
750 // which does not take arguments, a la GNU.
751 if(m_CanUsePing
== -1) // unknown
753 if(wxFileExists("/bin/ping"))
754 m_PingPath
= "/bin/ping";
755 else if(wxFileExists("/usr/sbin/ping"))
756 m_PingPath
= "/usr/sbin/ping";
765 // we didn't find ping
769 wxLogNull ln
; // suppress all error messages
770 wxASSERT(m_PingPath
.length());
772 cmd
<< m_PingPath
<< ' ';
773 #if defined(__SOLARIS__) || defined (__SUNOS__)
774 // nothing to add to ping command
775 #elif defined(__LINUX__) || defined ( __FREEBSD__)
776 cmd
<< "-c 1 "; // only ping once
777 #elif defined(__HPUX__)
778 cmd
<< "64 1 "; // only ping once (need also specify the packet size)
780 # pragma warning "No Ping information for this OS."
785 if(wxExecute(cmd
, TRUE
/* sync */) == 0)
786 return Net_Connected
;
792 wxDialUpManager
*wxDialUpManager::Create()
794 return new wxDialUpManagerImpl
;
797 #endif // wxUSE_DIALUP_MANAGER