1// -*- c++ -*- ////////////////////////////////////////////////////////////////
2// Name:        src/unix/dialup.cpp
3// Purpose:     Network related wxWidgets classes and functions
4// Author:      Karsten Ball�der
5// Modified by:
6// Created:     03.10.99
7// RCS-ID:      $Id: dialup.cpp 41020 2006-09-05 20:47:48Z VZ $
8// Copyright:   (c) Karsten Ball�der
9// Licence:     wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// for compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#if wxUSE_DIALUP_MANAGER
16
17#include "wx/dialup.h"
18
19#ifndef  WX_PRECOMP
20    #include "wx/string.h"
21    #include "wx/intl.h"
22    #include "wx/log.h"
23    #include "wx/event.h"
24    #include "wx/app.h"
25    #include "wx/utils.h"
26    #include "wx/timer.h"
27#endif // !PCH
28
29#include "wx/filefn.h"
30#include "wx/ffile.h"
31#include "wx/process.h"
32#include "wx/wxchar.h"
33
34#include <stdlib.h>
35
36#include <signal.h>
37#include <fcntl.h>
38#include <unistd.h>
39#define __STRICT_ANSI__
40#include <sys/socket.h>
41#include <netdb.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44#include <errno.h>
45
46DEFINE_EVENT_TYPE(wxEVT_DIALUP_CONNECTED)
47DEFINE_EVENT_TYPE(wxEVT_DIALUP_DISCONNECTED)
48
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// ----------------------------------------------------------------------------
54
55/* TODO
56 *
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
64 *    longs may be?)
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
69 *    main thread?
70 */
71
72class WXDLLEXPORT wxDialUpManagerImpl : public wxDialUpManager
73{
74public:
75   wxDialUpManagerImpl();
76   virtual ~wxDialUpManagerImpl();
77
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.
82   */
83   virtual bool IsOk() const
84      { return true; }
85
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
93   */
94   virtual bool Dial(const wxString& nameOfISP,
95                     const wxString& WXUNUSED(username),
96                     const wxString& WXUNUSED(password),
97                     bool async);
98
99   // Hang up the currently active dial up connection.
100   virtual bool HangUp();
101
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
106      {
107         CheckStatus();
108         return m_IsOnline == Net_Connected;
109      }
110
111   // do we have a constant net connection?
112   virtual bool IsAlwaysOnline() const;
113
114   // returns true if (async) dialing is in progress
115   virtual bool IsDialing() const
116      { return m_DialProcess != NULL; }
117
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();
121
122   size_t GetISPNames(class wxArrayString &) const
123      { return 0; }
124
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; }
131
132   // set misc wxDialUpManager options
133   // --------------------------------
134
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
139   // instantenous.
140   //
141   // Returns false if couldn't set up automatic check for online status.
142   virtual bool EnableAutoCheckOnlineStatus(size_t nSeconds);
143
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();
147
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,
152                                 int portno = 80);
153   /** Sets the commands to start up the network and to hang up
154       again. Used by the Unix implementations only.
155   */
156   virtual void SetConnectCommand(const wxString &command, const wxString &hupcmd)
157      { m_ConnectCommand = command; m_HangUpCommand = hupcmd; }
158
159//private: -- Sun CC 4.2 objects to using NetConnection enum as the return
160//            type if it is declared private
161
162   // the possible results of testing for Online() status
163   enum NetConnection
164   {
165       Net_Unknown = -1,    // we couldn't learn anything
166       Net_No,              // no network connection [currently]
167       Net_Connected        // currently connected
168   };
169
170   // the possible net connection types
171   enum NetDeviceType
172   {
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
177   };
178
179private:
180   // the current status
181   NetConnection m_IsOnline;
182
183   // the connection we have with the network card
184   NetConnection m_connCard;
185
186   // Can we use ifconfig to list active devices?
187   int m_CanUseIfconfig;
188
189   // The path to ifconfig
190   wxString m_IfconfigPath;
191
192   //  Can we use ping to find hosts?
193   int m_CanUsePing;
194   // The path to ping program
195   wxString m_PingPath;
196
197   // beacon host:
198   wxString m_BeaconHost;
199   // beacon host portnumber for connect:
200   int m_BeaconPort;
201
202   // command to connect to network
203   wxString m_ConnectCommand;
204   // command to hang up
205   wxString m_HangUpCommand;
206   // name of ISP
207   wxString m_ISPname;
208   // a timer for regular testing
209   class AutoCheckTimer *m_timer;
210   friend class AutoCheckTimer;
211
212   // a wxProcess for dialling in background
213   class wxDialProcess *m_DialProcess;
214   // pid of dial process
215   int m_DialPId;
216   friend class wxDialProcess;
217
218   // determine status
219   void CheckStatus(bool fromAsync = false) const;
220
221   // real status check
222   void CheckStatusInternal();
223
224   // check /proc/net (Linux only) for ppp/eth interfaces, returns the bit
225   // mask of NetDeviceType constants
226   int CheckProcNet();
227
228   // check output of ifconfig command for PPP/SLIP/PLIP devices, returns the
229   // bit mask of NetDeviceType constants
230   int CheckIfconfig();
231
232   // combines the 2 possible checks for determining the connection status
233   NetConnection CheckConnectAndPing();
234
235   // pings a host
236   NetConnection CheckPing();
237
238   // check by connecting to host on given port.
239   NetConnection CheckConnect();
240};
241
242
243class AutoCheckTimer : public wxTimer
244{
245public:
246   AutoCheckTimer(wxDialUpManagerImpl *dupman)
247   {
248       m_dupman = dupman;
249   }
250
251   virtual void Notify()
252   {
253       wxLogTrace(_T("dialup"), wxT("Checking dial up network status."));
254
255       m_dupman->CheckStatus();
256   }
257
258public:
259   wxDialUpManagerImpl *m_dupman;
260};
261
262class wxDialProcess : public wxProcess
263{
264public:
265   wxDialProcess(wxDialUpManagerImpl *dupman)
266      {
267         m_DupMan = dupman;
268      }
269   void Disconnect() { m_DupMan = NULL; }
270   virtual void OnTerminate(int WXUNUSED(pid), int WXUNUSED(status))
271      {
272         if(m_DupMan)
273         {
274            m_DupMan->m_DialProcess = NULL;
275            m_DupMan->CheckStatus(true);
276         }
277      }
278private:
279      wxDialUpManagerImpl *m_DupMan;
280};
281
282
283wxDialUpManagerImpl::wxDialUpManagerImpl()
284{
285   m_IsOnline =
286   m_connCard = Net_Unknown;
287   m_DialProcess = NULL;
288   m_timer = NULL;
289   m_CanUseIfconfig = -1; // unknown
290   m_CanUsePing = -1; // unknown
291   m_BeaconHost = WXDIALUP_MANAGER_DEFAULT_BEACONHOST;
292   m_BeaconPort = 80;
293
294#ifdef __SGI__
295   m_ConnectCommand = _T("/usr/etc/ppp");
296#elif defined(__LINUX__)
297   // default values for Debian/GNU linux
298   m_ConnectCommand = _T("pon");
299   m_HangUpCommand = _T("poff");
300#endif
301
302   wxChar * dial = wxGetenv(_T("WXDIALUP_DIALCMD"));
303   wxChar * hup = wxGetenv(_T("WXDIALUP_HUPCMD"));
304   SetConnectCommand(dial ? wxString(dial) : m_ConnectCommand,
305                     hup ? wxString(hup) : m_HangUpCommand);
306}
307
308wxDialUpManagerImpl::~wxDialUpManagerImpl()
309{
310   if(m_timer) delete m_timer;
311   if(m_DialProcess)
312   {
313      m_DialProcess->Disconnect();
314      m_DialProcess->Detach();
315   }
316}
317
318bool
319wxDialUpManagerImpl::Dial(const wxString &isp,
320                          const wxString & WXUNUSED(username),
321                          const wxString & WXUNUSED(password),
322                          bool async)
323{
324    if(m_IsOnline == Net_Connected)
325        return false;
326    m_ISPname = isp;
327    wxString cmd;
328    if(m_ConnectCommand.Find(wxT("%s")))
329        cmd.Printf(m_ConnectCommand,m_ISPname.c_str());
330    else
331        cmd = m_ConnectCommand;
332
333    if ( async )
334    {
335        m_DialProcess = new wxDialProcess(this);
336        m_DialPId = (int)wxExecute(cmd, false, m_DialProcess);
337        if(m_DialPId == 0)
338        {
339            delete m_DialProcess;
340            m_DialProcess = NULL;
341            return false;
342        }
343        else
344            return true;
345    }
346    else
347        return wxExecute(cmd, /* sync */ true) == 0;
348}
349
350bool wxDialUpManagerImpl::HangUp()
351{
352    if(m_IsOnline == Net_No)
353        return false;
354    if(IsDialing())
355    {
356        wxLogError(_("Already dialling ISP."));
357        return false;
358    }
359    wxString cmd;
360    if(m_HangUpCommand.Find(wxT("%s")))
361        cmd.Printf(m_HangUpCommand,m_ISPname.c_str(), m_DialProcess);
362    else
363        cmd = m_HangUpCommand;
364    return wxExecute(cmd, /* sync */ true) == 0;
365}
366
367
368bool wxDialUpManagerImpl::CancelDialing()
369{
370   if(! IsDialing())
371      return false;
372   return kill(m_DialPId, SIGTERM) > 0;
373}
374
375bool wxDialUpManagerImpl::EnableAutoCheckOnlineStatus(size_t nSeconds)
376{
377   DisableAutoCheckOnlineStatus();
378   m_timer = new AutoCheckTimer(this);
379   bool rc = m_timer->Start(nSeconds*1000);
380   if(! rc)
381   {
382      delete m_timer;
383      m_timer = NULL;
384   }
385   return rc;
386}
387
388void wxDialUpManagerImpl::DisableAutoCheckOnlineStatus()
389{
390   if(m_timer != NULL)
391   {
392      m_timer->Stop();
393      delete m_timer;
394      m_timer = NULL;
395   }
396}
397
398
399void wxDialUpManagerImpl::SetWellKnownHost(const wxString& hostname, int portno)
400{
401   if(hostname.length() == 0)
402   {
403      m_BeaconHost = WXDIALUP_MANAGER_DEFAULT_BEACONHOST;
404      m_BeaconPort = 80;
405      return;
406   }
407
408   // does hostname contain a port number?
409   wxString port = hostname.After(wxT(':'));
410   if(port.length())
411   {
412      m_BeaconHost = hostname.Before(wxT(':'));
413      m_BeaconPort = wxAtoi(port);
414   }
415   else
416   {
417      m_BeaconHost = hostname;
418      m_BeaconPort = portno;
419   }
420}
421
422
423void wxDialUpManagerImpl::CheckStatus(bool fromAsync) const
424{
425    // This function calls the CheckStatusInternal() helper function
426    // which is OS - specific and then sends the events.
427
428    NetConnection oldIsOnline = m_IsOnline;
429    ( /* non-const */ (wxDialUpManagerImpl *)this)->CheckStatusInternal();
430
431    // now send the events as appropriate: i.e. if the status changed and
432    // if we're in defined state
433    if(m_IsOnline != oldIsOnline
434            && m_IsOnline != Net_Unknown
435            && oldIsOnline != Net_Unknown )
436    {
437        wxDialUpEvent event(m_IsOnline == Net_Connected, ! fromAsync);
438        (void)wxTheApp->ProcessEvent(event);
439    }
440}
441
442/*
443   We first try to find out if ppp interface is active. If it is, we assume
444   that we're online but don't have a permanent connection (this is false if a
445   networked machine uses modem to connect to somewhere else, but we can't do
446   anything in this case anyhow).
447
448   If no ppp interface is detected, we check for eth interface. If it is
449   found, we check that we can, indeed, connect to an Internet host. The logic
450   here is that connection check should be fast enough in this case and we
451   don't want to give false positives in a (common) case of a machine on a LAN
452   which is not connected to the outside.
453
454   If we didn't find either ppp or eth interfaces, we stop here and decide
455   that we're connected. However, if couldn't check for this, we try to ping a
456   remote host just in case.
457
458   NB1: Checking for the interface presence can be done in 2 ways
459        a) reading /proc/net/dev under Linux
460        b) spawning ifconfig under any OS
461
462        The first method is faster but only works under Linux.
463
464   NB2: pinging, actually, means that we first try to connect "manually" to
465        a port on remove machine and if it fails, we run ping.
466*/
467
468void wxDialUpManagerImpl::CheckStatusInternal()
469{
470    m_IsOnline = Net_Unknown;
471
472    // first do quick checks to determine what kind of network devices do we
473    // have
474    int netDeviceType = CheckProcNet();
475    if ( netDeviceType == NetDevice_Unknown )
476    {
477        // nothing found, try ifconfig too
478        netDeviceType = CheckIfconfig();
479    }
480
481    switch ( netDeviceType )
482    {
483        case NetDevice_None:
484            // no network devices, no connection
485            m_IsOnline = Net_No;
486            break;
487
488        case NetDevice_LAN:
489            // we still do ping to confirm that we're connected but we only do
490            // it once and hope that the purpose of the network card (i.e.
491            // whether it used for connecting to the Internet or just to a
492            // LAN) won't change during the program lifetime
493            if ( m_connCard == Net_Unknown )
494            {
495                m_connCard = CheckConnectAndPing();
496            }
497            m_IsOnline = m_connCard;
498            break;
499
500        case NetDevice_Unknown:
501            // try to ping just in case
502            m_IsOnline = CheckConnectAndPing();
503            break;
504
505        case NetDevice_LAN + NetDevice_Modem:
506        case NetDevice_Modem:
507            // assume we're connected
508            m_IsOnline = Net_Connected;
509            break;
510
511        default:
512            wxFAIL_MSG(_T("Unexpected netDeviceType"));
513    }
514}
515
516bool wxDialUpManagerImpl::IsAlwaysOnline() const
517{
518    wxDialUpManagerImpl *self = wxConstCast(this, wxDialUpManagerImpl);
519
520    int netDeviceType = self->CheckProcNet();
521    if ( netDeviceType == NetDevice_Unknown )
522    {
523        // nothing found, try ifconfig too
524        netDeviceType = self->CheckIfconfig();
525    }
526
527    if ( netDeviceType == NetDevice_Unknown )
528    {
529        // this is the only thing we can do unfortunately...
530        self->HangUp();
531        return IsOnline();
532    }
533    else
534    {
535        // we are only permanently online if we have a network card
536        return (netDeviceType & NetDevice_LAN) != 0;
537    }
538}
539
540wxDialUpManagerImpl::NetConnection wxDialUpManagerImpl::CheckConnectAndPing()
541{
542    NetConnection conn;
543
544    // first try connecting - faster
545    conn = CheckConnect();
546    if ( conn == Net_Unknown )
547    {
548        // try pinging too
549        conn = CheckPing();
550    }
551
552    return conn;
553}
554
555wxDialUpManagerImpl::NetConnection wxDialUpManagerImpl::CheckConnect()
556{
557   // second method: try to connect to a well known host:
558   // This can be used under Win 9x, too!
559   struct hostent     *hp;
560   struct sockaddr_in  serv_addr;
561
562   if((hp = gethostbyname(m_BeaconHost.mb_str())) == NULL)
563      return Net_No; // no DNS no net
564
565   serv_addr.sin_family = hp->h_addrtype;
566   memcpy(&serv_addr.sin_addr,hp->h_addr, hp->h_length);
567   serv_addr.sin_port = htons(m_BeaconPort);
568
569   int sockfd;
570   if( ( sockfd = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
571   {
572      return Net_Unknown;  // no info
573   }
574
575   if( connect(sockfd, (struct sockaddr *) &serv_addr,
576               sizeof(serv_addr)) >= 0)
577   {
578      close(sockfd);
579      return Net_Connected; // we can connect, so we have a network!
580   }
581   else // failed to connect
582   {
583#ifdef ENETUNREACH
584       if(errno == ENETUNREACH)
585          return Net_No; // network is unreachable
586       else
587#endif
588          return Net_Unknown; // connect failed, but don't know why
589   }
590}
591
592
593int
594wxDialUpManagerImpl::CheckProcNet()
595{
596    // assume that the test doesn't work
597    int netDevice = NetDevice_Unknown;
598
599#ifdef __LINUX__
600    if (wxFileExists(_T("/proc/net/route")))
601    {
602        // cannot use wxFile::Length because file doesn't support seeking, so
603        // use stdio directly
604        FILE *f = fopen("/proc/net/route", "rt");
605        if (f != NULL)
606        {
607            // now we know that we will find all devices we may have
608            netDevice = NetDevice_None;
609
610            char output[256];
611
612            while (fgets(output, 256, f) != NULL)
613            {
614                if ( strstr(output, "eth") ) // network card
615                {
616                    netDevice |= NetDevice_LAN;
617                }
618                else if (strstr(output,"ppp")   // ppp
619                        || strstr(output,"sl")  // slip
620                        || strstr(output,"pl")) // plip
621                {
622                    netDevice |= NetDevice_Modem;
623                }
624            }
625
626            fclose(f);
627        }
628    }
629#endif // __LINUX__
630
631    return netDevice;
632}
633
634
635int
636wxDialUpManagerImpl::CheckIfconfig()
637{
638#ifdef __VMS
639    m_CanUseIfconfig = 0;
640    return -1;
641#else
642    // assume that the test doesn't work
643    int netDevice = NetDevice_Unknown;
644
645    // first time check for ifconfig location
646    if ( m_CanUseIfconfig == -1 ) // unknown
647    {
648        static const wxChar *ifconfigLocations[] =
649        {
650            _T("/sbin"),         // Linux, FreeBSD, Darwin
651            _T("/usr/sbin"),     // SunOS, Solaris, AIX, HP-UX
652            _T("/usr/etc"),      // IRIX
653            _T("/etc"),          // AIX 5
654        };
655
656        for ( size_t n = 0; n < WXSIZEOF(ifconfigLocations); n++ )
657        {
658            wxString path(ifconfigLocations[n]);
659            path << _T("/ifconfig");
660
661            if ( wxFileExists(path) )
662            {
663                m_IfconfigPath = path;
664                break;
665            }
666        }
667    }
668
669    if ( m_CanUseIfconfig != 0 ) // unknown or yes
670    {
671        wxLogNull ln; // suppress all error messages
672
673        wxASSERT_MSG( m_IfconfigPath.length(),
674                      _T("can't use ifconfig if it wasn't found") );
675
676        wxString tmpfile = wxGetTempFileName( wxT("_wxdialuptest") );
677        wxString cmd = wxT("/bin/sh -c \'");
678        cmd << m_IfconfigPath;
679#if defined(__AIX__) || \
680    defined(__OSF__) || \
681    defined(__SOLARIS__) || defined (__SUNOS__)
682        // need to add -a flag
683        cmd << wxT(" -a");
684#elif defined(__LINUX__) || defined(__SGI__)
685        // nothing to be added to ifconfig
686#elif defined(__FREEBSD__) || defined(__DARWIN__)
687        // add -l flag
688        cmd << wxT(" -l");
689#elif defined(__HPUX__)
690        // VZ: a wild guess (but without it, ifconfig fails completely)
691        cmd << wxT(" ppp0");
692#else
693        #if defined(__GNUG__)
694            #warning "No ifconfig information for this OS."
695        #else
696            #pragma warning "No ifconfig information for this OS."
697        #endif
698
699        m_CanUseIfconfig = 0;
700        return -1;
701#endif
702       cmd << wxT(" >") << tmpfile <<  wxT('\'');
703        /* I tried to add an option to wxExecute() to not close stdout,
704           so we could let ifconfig write directly to the tmpfile, but
705           this does not work. That should be faster, as it doesn�t call
706           the shell first. I have no idea why. :-(  (KB) */
707        if ( wxExecute(cmd,true /* sync */) == 0 )
708        {
709            m_CanUseIfconfig = 1;
710            wxFFile file;
711            if( file.Open(tmpfile) )
712            {
713                wxString output;
714                if ( file.ReadAll(&output) )
715                {
716                    // FIXME shouldn't we grep for "^ppp"? (VZ)
717
718                    bool hasModem = false,
719                         hasLAN = false;
720
721#if defined(__SOLARIS__) || defined (__SUNOS__)
722                    // dialup device under SunOS/Solaris
723                    hasModem = strstr(output.fn_str(),"ipdptp") != (char *)NULL;
724                    hasLAN = strstr(output.fn_str(), "hme") != (char *)NULL;
725#elif defined(__LINUX__) || defined (__FREEBSD__)
726                    hasModem = strstr(output.fn_str(),"ppp")    // ppp
727                        || strstr(output.fn_str(),"sl")  // slip
728                        || strstr(output.fn_str(),"pl"); // plip
729                    hasLAN = strstr(output.fn_str(), "eth") != NULL;
730#elif defined(__SGI__)  // IRIX
731                    hasModem = strstr(output.fn_str(), "ppp") != NULL; // PPP
732#elif defined(__HPUX__)
733                    // if could run ifconfig on interface, then it exists
734                    hasModem = true;
735#endif
736
737                    netDevice = NetDevice_None;
738                    if ( hasModem )
739                        netDevice |= NetDevice_Modem;
740                    if ( hasLAN )
741                        netDevice |= NetDevice_LAN;
742                }
743                //else: error reading the file
744            }
745            //else: error opening the file
746        }
747        else // could not run ifconfig correctly
748        {
749            m_CanUseIfconfig = 0; // don�t try again
750        }
751
752        (void) wxRemoveFile(tmpfile);
753    }
754
755    return netDevice;
756#endif
757}
758
759wxDialUpManagerImpl::NetConnection wxDialUpManagerImpl::CheckPing()
760{
761    // First time check for ping location. We only use the variant
762    // which does not take arguments, a la GNU.
763    if(m_CanUsePing == -1) // unknown
764    {
765#ifdef __VMS
766        if (wxFileExists( wxT("SYS$SYSTEM:TCPIP$PING.EXE") ))
767            m_PingPath = wxT("$SYS$SYSTEM:TCPIP$PING");
768#elif defined(__AIX__)
769        m_PingPath = _T("/etc/ping");
770#elif defined(__SGI__)
771        m_PingPath = _T("/usr/etc/ping");
772#else
773        if (wxFileExists( wxT("/bin/ping") ))
774            m_PingPath = wxT("/bin/ping");
775        else if (wxFileExists( wxT("/usr/sbin/ping") ))
776            m_PingPath = wxT("/usr/sbin/ping");
777#endif
778        if (!m_PingPath)
779        {
780            m_CanUsePing = 0;
781        }
782    }
783
784    if(! m_CanUsePing)
785    {
786       // we didn't find ping
787       return Net_Unknown;
788    }
789
790    wxLogNull ln; // suppress all error messages
791    wxASSERT(m_PingPath.length());
792    wxString cmd;
793    cmd << m_PingPath << wxT(' ');
794#if defined(__SOLARIS__) || defined (__SUNOS__)
795    // nothing to add to ping command
796#elif defined(__AIX__) || \
797      defined (__BSD__) || \
798      defined(__LINUX__) || \
799      defined(__OSF__) || \
800      defined(__SGI__) || \
801      defined(__VMS)
802    cmd << wxT("-c 1 "); // only ping once
803#elif defined(__HPUX__)
804    cmd << wxT("64 1 "); // only ping once (need also specify the packet size)
805#else
806    #if defined(__GNUG__)
807        #warning "No Ping information for this OS."
808    #else
809        #pragma warning "No Ping information for this OS."
810    #endif
811
812    m_CanUsePing = 0;
813    return Net_Unknown;
814#endif
815    cmd << m_BeaconHost;
816    if(wxExecute(cmd, true /* sync */) == 0)
817        return Net_Connected;
818    else
819        return Net_No;
820}
821
822/* static */
823wxDialUpManager *wxDialUpManager::Create()
824{
825   return new wxDialUpManagerImpl;
826}
827
828#endif // wxUSE_DIALUP_MANAGER
829