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
> 0;
111 /// do we have a constant net connection? -- GUESS!
112 bool IsAlwaysOnline() const
114 ((wxDialUpManagerImpl
*) this)->HangUp(); // brutal but necessary
117 /// returns TRUE if (async) dialing is in progress
118 inline virtual bool IsDialing() const
119 { return m_DialProcess
!= NULL
; }
121 // cancel dialing the number initiated with Dial(async = TRUE)
122 // NB: this won't result in DISCONNECTED event being sent
123 virtual bool CancelDialing();
125 size_t GetISPNames(class wxArrayString
&) const
128 // sometimes the built-in logic for determining the online status may fail,
129 // so, in general, the user should be allowed to override it. This function
130 // allows to forcefully set the online status - whatever our internal
131 // algorithm may think about it.
132 virtual void SetOnlineStatus(bool isOnline
= TRUE
)
133 { m_IsOnline
= isOnline
; }
135 // set misc wxDialUpManager options
136 // --------------------------------
138 // enable automatical checks for the connection status and sending of
139 // wxEVT_DIALUP_CONNECTED/wxEVT_DIALUP_DISCONNECTED events. The interval
140 // parameter is only for Unix where we do the check manually: under
141 // Windows, the notification about the change of connection status is
144 // Returns FALSE if couldn't set up automatic check for online status.
145 virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds
);
147 // disable automatic check for connection status change - notice that the
148 // wxEVT_DIALUP_XXX events won't be sent any more neither.
149 virtual void DisableAutoCheckOnlineStatus();
151 // under Unix, the value of well-known host is used to check whether we're
152 // connected to the internet. It's unused under Windows, but this function
153 // is always safe to call. The default value is www.yahoo.com.
154 virtual void SetWellKnownHost(const wxString
& hostname
,
156 /** Sets the commands to start up the network and to hang up
157 again. Used by the Unix implementations only.
159 virtual void SetConnectCommand(const wxString
&command
, const wxString
&hupcmd
)
160 { m_ConnectCommand
= command
; m_HangUpCommand
= hupcmd
; }
163 /// -1: don´t know, 0 = no, 1 = yes
166 /// Can we use ifconfig to list active devices?
167 int m_CanUseIfconfig
;
168 /// The path to ifconfig
169 wxString m_IfconfigPath
;
171 /// Can we use ping to find hosts?
173 /// The path to ping program
177 wxString m_BeaconHost
;
178 /// beacon host portnumber for connect:
181 /// command to connect to network
182 wxString m_ConnectCommand
;
183 /// command to hang up
184 wxString m_HangUpCommand
;
187 /// a timer for regular testing
188 class AutoCheckTimer
*m_timer
;
189 friend class AutoCheckTimer
;
191 /// a wxProcess for dialling in background
192 class wxDialProcess
*m_DialProcess
;
193 /// pid of dial process
195 friend class wxDialProcess
;
198 void CheckStatus(bool fromAsync
= FALSE
) const;
200 /// real status check
201 void CheckStatusInternal(void);
203 /// Check /proc/net (Linux only)
204 int CheckProcNet(void);
205 /// Check output of ifconfig command for PPP/SLIP/PLIP devices
206 int CheckIfconfig(void);
207 /// Ping a host: 1 on success, -1 if it cannot be used, 0 if unreachable
209 /// Check by connecting to host on given port.
210 int CheckConnect(void);
215 class AutoCheckTimer
: public wxTimer
218 AutoCheckTimer(wxDialUpManagerImpl
*dupman
)
224 virtual bool Start( int millisecs
= -1, bool WXUNUSED(one_shot
) = FALSE
)
225 { m_started
= TRUE
; return wxTimer::Start(millisecs
, FALSE
); }
227 virtual void Notify()
228 { wxLogTrace(wxT("Checking dial up network status.")); m_dupman
->CheckStatus(); }
231 { if ( m_started
) wxTimer::Stop(); }
234 wxDialUpManagerImpl
*m_dupman
;
237 class wxDialProcess
: public wxProcess
240 wxDialProcess(wxDialUpManagerImpl
*dupman
)
244 void Disconnect(void) { m_DupMan
= NULL
; }
245 virtual void OnTerminate(int WXUNUSED(pid
), int WXUNUSED(status
))
249 m_DupMan
->m_DialProcess
= NULL
;
250 m_DupMan
->CheckStatus(TRUE
);
254 wxDialUpManagerImpl
*m_DupMan
;
258 wxDialUpManagerImpl::wxDialUpManagerImpl()
260 /* The isOnline flag can have the following values internally:
263 -1 : unknown/undefined status
266 m_DialProcess
= NULL
;
268 m_CanUseIfconfig
= -1; // unknown
269 m_CanUsePing
= -1; // unknown
270 m_BeaconHost
= WXDIALUP_MANAGER_DEFAULT_BEACONHOST
;
274 m_ConnectCommand
= _T("/usr/etc/ppp");
275 #elif defined(__LINUX__)
276 // default values for Debian/GNU linux
277 m_ConnectCommand
= _T("pon");
278 m_HangUpCommand
= _T("poff");
281 wxChar
* dial
= wxGetenv(_T("WXDIALUP_DIALCMD"));
282 wxChar
* hup
= wxGetenv(_T("WXDIALUP_HUPCMD"));
283 SetConnectCommand(dial
? wxString(dial
) : m_ConnectCommand
,
284 hup
? wxString(hup
) : m_HangUpCommand
);
287 wxDialUpManagerImpl::~wxDialUpManagerImpl()
289 if(m_timer
) delete m_timer
;
292 m_DialProcess
->Disconnect();
293 m_DialProcess
->Detach();
298 wxDialUpManagerImpl::Dial(const wxString
&isp
,
299 const wxString
& WXUNUSED(username
),
300 const wxString
& WXUNUSED(password
),
307 if(m_ConnectCommand
.Find(wxT("%s")))
308 cmd
.Printf(m_ConnectCommand
,m_ISPname
.c_str());
310 cmd
= m_ConnectCommand
;
314 m_DialProcess
= new wxDialProcess(this);
315 m_DialPId
= (int)wxExecute(cmd
, FALSE
, m_DialProcess
);
318 delete m_DialProcess
;
319 m_DialProcess
= NULL
;
326 return wxExecute(cmd
, /* sync */ TRUE
) == 0;
330 wxDialUpManagerImpl::HangUp(void)
336 wxLogError(_("Already dialling ISP."));
340 if(m_HangUpCommand
.Find(wxT("%s")))
341 cmd
.Printf(m_HangUpCommand
,m_ISPname
.c_str(), m_DialProcess
);
343 cmd
= m_HangUpCommand
;
344 return wxExecute(cmd
, /* sync */ TRUE
) == 0;
349 wxDialUpManagerImpl::CancelDialing()
353 return kill(m_DialPId
, SIGTERM
) > 0;
357 wxDialUpManagerImpl::EnableAutoCheckOnlineStatus(size_t nSeconds
)
359 DisableAutoCheckOnlineStatus();
360 m_timer
= new AutoCheckTimer(this);
361 bool rc
= m_timer
->Start(nSeconds
*1000);
371 wxDialUpManagerImpl::DisableAutoCheckOnlineStatus()
383 wxDialUpManagerImpl::SetWellKnownHost(const wxString
& hostname
, int portno
)
385 if(hostname
.Length() == 0)
387 m_BeaconHost
= WXDIALUP_MANAGER_DEFAULT_BEACONHOST
;
392 /// does hostname contain a port number?
393 wxString port
= hostname
.After(wxT(':'));
396 m_BeaconHost
= hostname
.Before(wxT(':'));
397 m_BeaconPort
= wxAtoi(port
);
401 m_BeaconHost
= hostname
;
402 m_BeaconPort
= portno
;
408 wxDialUpManagerImpl::CheckStatus(bool fromAsync
) const
410 // This function calls the CheckStatusInternal() helper function
411 // which is OS - specific and then sends the events.
413 int oldIsOnline
= m_IsOnline
;
414 ( /* non-const */ (wxDialUpManagerImpl
*)this)->CheckStatusInternal();
416 // now send the events as appropriate:
417 if(m_IsOnline
!= oldIsOnline
// it changed
418 && ( m_IsOnline
== 1 // and it is a defined status
420 // only send events for well defined transitions
421 && ( oldIsOnline
== 1 || oldIsOnline
== 0)
424 wxDialUpEvent
event(m_IsOnline
, ! fromAsync
);
425 (void)wxTheApp
->ProcessEvent(event
);
430 We have three methods that we can use:
432 1. test via /sbin/ifconfig and grep for "sl", "ppp", "pl"
433 --> should be fast enough for regular polling
434 2. test if we can reach the well known beacon host
435 --> too slow for polling
436 3. check /proc/net/dev on linux??
437 This method should be preferred, if possible. Need to do more
443 wxDialUpManagerImpl::CheckStatusInternal(void)
449 testResult
= CheckProcNet();
451 testResult
= CheckIfconfig();
453 testResult
= CheckConnect();
455 testResult
= CheckPing();
456 m_IsOnline
= testResult
;
460 wxDialUpManagerImpl::CheckConnect(void)
462 // second method: try to connect to a well known host:
463 // This can be used under Win 9x, too!
465 struct sockaddr_in serv_addr
;
467 if((hp
= gethostbyname(m_BeaconHost
.mb_str())) == NULL
)
468 return 0; // no DNS no net
470 serv_addr
.sin_family
= hp
->h_addrtype
;
471 memcpy(&serv_addr
.sin_addr
,hp
->h_addr
, hp
->h_length
);
472 serv_addr
.sin_port
= htons(m_BeaconPort
);
475 if( ( sockfd
= socket(hp
->h_addrtype
, SOCK_STREAM
, 0)) < 0)
477 return -1; // no info
480 if( connect(sockfd
, (struct sockaddr
*) &serv_addr
,
481 sizeof(serv_addr
)) >= 0)
484 return 1; // we cant connect, so we have a network!
488 if(errno
== ENETUNREACH
)
489 return 0; // network is unreachable
490 // connect failed, but don't know why
496 wxDialUpManagerImpl::CheckProcNet(void)
501 if (wxFileExists(_T("/proc/net/route")))
503 // NOTE: cannot use wxFile::Length because file doesn't support
505 FILE *f
= fopen("/proc/net/route", "rt");
510 while (fgets(output
, 256, f
) != NULL
)
512 if (strstr(output
,"ppp") // ppp
513 || strstr(output
,"sl") // slip
514 || strstr(output
,"pl")) // plip
517 if (rc
== -1) rc
= 0;
528 wxDialUpManagerImpl::CheckIfconfig(void)
532 // First time check for ifconfig location. We only use the variant which
533 // does not take arguments, a la GNU.
534 if ( m_CanUseIfconfig
== -1 ) // unknown
536 static const wxChar
*ifconfigLocations
[] =
538 _T("/sbin"), // Linux, FreeBSD
539 _T("/usr/sbin"), // SunOS, Solaris, AIX, HP-UX
540 _T("/usr/etc"), // IRIX
543 for ( size_t n
= 0; n
< WXSIZEOF(ifconfigLocations
); n
++ )
545 wxString
path(ifconfigLocations
[n
]);
546 path
<< _T("/ifconfig");
548 if ( wxFileExists(path
) )
550 m_IfconfigPath
= path
;
556 wxLogNull ln
; // suppress all error messages
557 // Let´s try the ifconfig method first, should be fastest:
558 if(m_CanUseIfconfig
!= 0) // unknown or yes
560 wxASSERT(m_IfconfigPath
.length());
562 wxString tmpfile
= wxGetTempFileName("_wxdialuptest");
563 wxString cmd
= "/bin/sh -c \'";
564 cmd
<< m_IfconfigPath
;
565 #if defined(__SOLARIS__) || defined (__SUNOS__)
566 // need to add -a flag
568 #elif defined(__LINUX__) || defined(__SGI__)
569 // nothing to be added to ifconfig
570 #elif defined(__FREEBSD__)
573 #elif defined(__HPUX__)
574 // VZ: a wild guess (but without it, ifconfig fails completely)
577 # pragma warning "No ifconfig information for this OS."
578 m_CanUseIfconfig
= 0;
581 cmd
<< " >" << tmpfile
<< '\'';
582 /* I tried to add an option to wxExecute() to not close stdout,
583 so we could let ifconfig write directly to the tmpfile, but
584 this does not work. That should be faster, as it doesn´t call
585 the shell first. I have no idea why. :-( (KB) */
586 if(wxExecute(cmd
,TRUE
/* sync */) == 0)
588 m_CanUseIfconfig
= 1;
590 if( file
.Open(tmpfile
) )
592 char *output
= new char [file
.Length()+1];
593 output
[file
.Length()] = '\0';
594 if(file
.Read(output
,file
.Length()) == file
.Length())
596 // FIXME shouldn't we grep for "^ppp"? (VZ)
598 #if defined(__SOLARIS__) || defined (__SUNOS__)
599 // dialup device under SunOS/Solaris
600 rc
= strstr(output
,"ipdptp") != (char *)NULL
;
601 #elif defined(__LINUX__) || defined (__FREEBSD__)
602 rc
= strstr(output
,"ppp") // ppp
603 || strstr(output
,"sl") // slip
604 || strstr(output
,"pl"); // plip
605 #elif defined(__SGI__) // IRIX
606 rc
= (int) strstr(output
, "ppp"); // PPP
607 #elif defined(__HPUX__)
608 // if could run ifconfig on interface, then it exists
615 // else rc remains -1 as we don't know for sure
617 else // could not run ifconfig correctly
618 m_CanUseIfconfig
= 0; // don´t try again
619 (void) wxRemoveFile(tmpfile
);
626 wxDialUpManagerImpl::CheckPing(void)
631 // First time check for ping location. We only use the variant
632 // which does not take arguments, a la GNU.
633 if(m_CanUsePing
== -1) // unknown
635 if(wxFileExists("/bin/ping"))
636 m_PingPath
= "/bin/ping";
637 else if(wxFileExists("/usr/sbin/ping"))
638 m_PingPath
= "/usr/sbin/ping";
646 wxLogNull ln
; // suppress all error messages
647 wxASSERT(m_PingPath
.length());
649 cmd
<< m_PingPath
<< ' ';
650 #if defined(__SOLARIS__) || defined (__SUNOS__)
651 // nothing to add to ping command
652 #elif defined(__LINUX__) || defined ( __FREEBSD__)
653 cmd
<< "-c 1 "; // only ping once
654 #elif defined(__HPUX__)
655 cmd
<< "64 1 "; // only ping once (need also specify the packet size)
657 # pragma warning "No Ping information for this OS."
662 if(wxExecute(cmd
, TRUE
/* sync */) == 0)
670 wxDialUpManager::Create(void)
672 return new wxDialUpManagerImpl
;
675 #endif // wxUSE_DIALUP_MANAGER