2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2003 Net Integration Technologies, Inc.
5 * Implementation of the WvDialer smart-dialer class.
12 #include <sys/types.h>
22 #include <xplc/xplc.h>
24 // N_() is defined here just for internationalization.
25 #define N_(String) String
27 static char * init_responses
[] = {
33 static char * dial_responses
[] = {
46 static char * prompt_strings
[] = {
52 static int messagetail_pid
= 0;
54 //**************************************************
55 // WvDialer Public Functions
56 //**************************************************
58 WvDialer::WvDialer( WvConf
&_cfg
, WvStringList
*_sect_list
, bool _chat_mode
)
59 /***************************************************************************/
61 cfg(_cfg
), log( "WvDial", WvLog::Debug
),
62 err( log
.split( WvLog::Error
) ),
63 modemrx( "WvDial Modem", WvLog::Debug
)
71 last_rx
= last_execute
= 0;
73 auto_reconnect_delay
= 0;
74 auto_reconnect_at
= 0;
78 // tell wvstreams we need our own subtask
79 uses_continue_select
= true;
83 sect_list
= _sect_list
;
84 chat_mode
= _chat_mode
;
86 log("WvDial: Internet dialer version " WVDIAL_VER_STRING
"\n");
88 // Ensure all sections in sect_list actually exist, warning if not.
89 WvStringList::Iter
iter(*sect_list
);
90 for(iter
.rewind(); iter
.next();)
92 if(cfg
[*iter
] == NULL
)
95 "Warning: section [%s] does not exist in wvdial.conf.\n",
100 // Ensure all inherited sections exist, warning if not.
101 WvConfigSectionList::Iter
iter2 (cfg
);
102 for (iter2
.rewind(); iter2
.next();)
104 WvConfigSection
& sect2
= *iter2
;
105 WvConfigEntry
* entry
= sect2
["Inherits"];
108 WvString inherits
= entry
->value
;
109 if (cfg
[inherits
] == NULL
)
111 "Warning: inherited section [%s] does not exist in wvdial.conf\n",
116 // Activate the brain and read configuration.
117 brain
= new WvDialBrain(this);
119 // init_modem() reads the config options. It MUST run here!
123 // init_modem() printed an error
128 if (options
.provider
.len())
130 log( WvLog::Notice
, "Dialing %s %s.\n",
134 if (options
.homepage
.len())
136 log(WvLog::Notice
, "Homepage of %s: %s\n",
137 options
.provider
.len() ? options
.provider
.cstr() : "this provider",
141 if(options
.auto_reconnect
&& options
.idle_seconds
> 0)
144 "Idle Seconds = %s, disabling automatic reconnect.\n",
145 options
.idle_seconds
);
146 options
.auto_reconnect
= false;
149 pppd_mon
.setdnstests(options
.dnstest1
, options
.dnstest2
);
150 pppd_mon
.setcheckdns(options
.check_dns
);
151 pppd_mon
.setcheckdfr(options
.check_dfr
);
154 WvDialer::~WvDialer()
155 /*******************/
157 terminate_continue_select();
164 bool WvDialer::dial()
165 /*******************/
166 // Returns false on error, or true to go asynchronous while dialing.
173 // error message has already been printed elsewhere
179 err( "Configuration does not specify a valid phone number.\n" );
185 err( "Configuration does not specify a valid login name.\n" );
189 if (!options
.password
)
191 err( "Configuration does not specify a valid password.\n" );
199 if(options
.phnum1
.len())
202 if(options
.phnum2
.len())
205 if(options
.phnum3
.len())
208 if(options
.phnum4
.len())
214 // we need to re-init the modem if we were online before.
215 if(been_online
&& !init_modem())
220 connect_attempts
= 1;
228 void WvDialer::hangup()
229 /*********************/
240 log( "Disconnecting at %s", ctime( &now
) );
245 if (messagetail_pid
> 0)
247 kill(messagetail_pid
, 15);
252 bool WvDialer::pre_select( SelectInfo
& si
)
253 /*******************************************/
255 if( isok() && stat
!= Online
&& stat
!= Idle
256 && time( NULL
) - last_execute
> 1 )
258 // Pretend we have "data ready," so execute() gets called.
259 // select() already returns true whenever the modem is readable,
260 // but when we are doing a timeout (eg. PreDial1/2) for example,
261 // we need to execute() even if no modem data is incoming.
266 return WvStreamClone::pre_select( si
);
270 bool WvDialer::isok() const
271 /*************************/
273 bool b
= ( !modem
|| modem
->isok() )
274 && stat
!= ModemError
&& stat
!= OtherError
;
277 err("Returning not ok!!\n" );
282 char *WvDialer::connect_status() const
283 /*************************************/
285 static char msg
[ 160 ];
294 strcpy( msg
, N_("Last attempt timed out. Trying again.") );
295 else if( dial_stat
== 2 )
296 strcpy( msg
, N_("Modem did not connect last attempt. Trying again.") );
297 else if( dial_stat
== 3 )
298 strcpy( msg
, N_("No dial tone last attempt. Trying again.") );
299 else if( dial_stat
== 4 )
300 strcpy( msg
, N_("Busy signal on last attempt. Trying again.") );
301 else if( dial_stat
== 5 )
302 strcpy( msg
, N_("Voice answer on last attempt. Trying again.") );
303 else if( dial_stat
== 6 )
304 strcpy( msg
, N_("Fax answer on last attempt. Trying again.") );
305 else if( dial_stat
== 7 )
306 strcpy( msg
, N_("No answer on last attempt. Trying again.") );
311 strcpy( msg
, N_("Waiting for a response from Internet Provider.") );
314 strcpy( msg
, N_("Waiting for a prompt from Internet Provider.") );
316 case AutoReconnectDelay
:
317 sprintf( msg
, "Next attempt in 00:%02ld:%02ld.",
318 ( auto_reconnect_at
- time( NULL
) ) / 60,
319 ( auto_reconnect_at
- time( NULL
) ) % 60 );
328 void WvDialer::pppd_watch( int ms
)
329 /*********************************/
331 // see if pppd has a message, analyse it and output to log
333 if( pppd_log
!= NULL
&& pppd_log
->isok() )
337 while ( (line
= pppd_log
->blocking_getline( ms
)) )
339 WvString
buffer1(pppd_mon
.analyse_line( line
));
342 log("pppd: %s\n", buffer1
);
349 void WvDialer::execute()
350 /**********************/
352 WvStreamClone::execute();
354 // the modem object might not exist, if we just disconnected and are
356 if( !modem
&& !init_modem() )
359 last_execute
= time( NULL
);
373 // we allow some time after connection for silly servers/modems.
374 if( modem
->select( 500, true, false ) )
376 // if any data comes in at all, switch to impatient mode.
378 last_rx
= time( NULL
);
380 else if( time( NULL
) - last_rx
>= 30 )
382 // timed out - do what WaitPrompt would do on a timeout.
387 // We prod the server with a CR character every once in a while.
388 // FIXME: Does this cause problems with login prompts?
389 modem
->write( "\r", 1 );
396 assert( !chat_mode
);
397 // If already online, we only need to make sure pppd is still there.
398 if( ppp_pipe
&& ppp_pipe
->child_exited() )
400 int pppd_exit_status
= ppp_pipe
->exit_status();
401 if( ppp_pipe
->child_killed() )
403 log( WvLog::Error
, "PPP was killed! (signal = %s)\n",
404 ppp_pipe
->exit_status() );
407 // we must delete the WvModem object so it can be recreated
408 // later; starting pppd seems to screw up the file descriptor.
412 time_t call_duration
= time( NULL
) - connected_at
;
414 if( pppd_mon
.auth_failed() )
416 log("Authentication error.\n"
417 "We failed to authenticate ourselves to the peer.\n"
418 "Maybe bad account or password?\n" );
423 switch (pppd_exit_status
)
426 msg
= "pppd options error";
429 msg
= "No root priv error";
432 msg
= "No ppp module error";
435 msg
= "PPP negotiation failed";
438 msg
= "Peer didn't authenticatie itself";
441 msg
= "Link idle: Idle Seconds reached.";
444 msg
= "Connect time limit reached.";
447 msg
= "Callback negotiated, call should come back.";
450 msg
= "Lack of LCP echo responses";
453 msg
= "Loopback detected";
456 msg
= "Authentication error.\n"
457 "We failed to authenticate ourselves to the peer.\n"
458 "Maybe bad account or password?";
463 // Note: exit code = %s is parsed by kinternet:
464 log("The PPP daemon has died: %s (exit code = %s)\n",
465 msg
, pppd_exit_status
);
466 log("man pppd explains pppd error codes in more detail.\n");
467 err(WvLog::Notice
, "I guess that's it for now, exiting\n");
468 if (pppd_exit_status
== 12 && options
.auto_reconnect
)
469 err(WvLog::Notice
, "Idle parameter is passed to pppd\n"
470 "If you don't want an idle timeout per default,\n"
471 "comment out the idle parameter in /etc/ppp/options\n");
472 if (pppd_exit_status
== 15)
474 log("Provider is overloaded(often the case) or line problem.\n");
476 options
.auto_reconnect
= false;
479 switch (pppd_exit_status
)
482 msg
= "Fatal pppd error";
485 msg
= "pppd received a signal";
488 msg
= "Serial port lock failed";
491 msg
= "Serial port open failed";
494 msg
= "Connect script failed";
497 msg
= "Pty program error";
500 msg
= "A modem hung up the phone";
503 msg
= "The init script failed";
508 log("The PPP daemon has died: %s (exit code = %s)\n",
509 msg
, pppd_exit_status
);
510 log("man pppd explains pppd error codes in more detail.\n");
511 log(WvLog::Notice
, "Try again and look into /var/log/messages "
512 "and the wvdial and pppd man pages for more information.\n");
514 log(WvLog::Notice
, "The PPP daemon has died. (exit code = %s)\n",
518 // check to see if we're supposed to redial automatically soon.
519 if( options
.auto_reconnect
&& isok() )
521 if( call_duration
>= 45 )
522 // Connection was more than 45 seconds, so reset the
523 // "exponential backup timer".
524 auto_reconnect_delay
= 0;
526 // exponentially back up...
527 auto_reconnect_delay
*= 2;
528 if( auto_reconnect_delay
== 0 )
529 auto_reconnect_delay
= 5; // start at 5 seconds
530 if( auto_reconnect_delay
> 600 )
531 auto_reconnect_delay
= 600; // no longer than 10 minutes
533 auto_reconnect_at
= time( NULL
) + auto_reconnect_delay
;
535 stat
= AutoReconnectDelay
;
536 log( WvLog::Notice
, "Auto Reconnect will be attempted in %s "
538 auto_reconnect_at
- time( NULL
) );
542 case AutoReconnectDelay
:
543 // If enough time has passed after ISP disconnected us that we should
545 // We can only get into this state if the Auto Reconnect option is
546 // enabled, so there's no point in checking the option here.
547 if( time( NULL
) >= auto_reconnect_at
)
563 //**************************************************
564 // WvDialer Private Functions
565 //**************************************************
567 void WvDialer::load_options()
568 /***************************/
572 { "Modem", &options
.modem
, NULL
, "/dev/modem", 0 },
573 { "Init1", &options
.init1
, NULL
, "ATZ", 0 },
574 { "Init2", &options
.init2
, NULL
, "", 0 },
575 { "Init3", &options
.init3
, NULL
, "", 0 },
576 { "Init4", &options
.init4
, NULL
, "", 0 },
577 { "Init5", &options
.init5
, NULL
, "", 0 },
578 { "Init6", &options
.init6
, NULL
, "", 0 },
579 { "Init7", &options
.init7
, NULL
, "", 0 },
580 { "Init8", &options
.init8
, NULL
, "", 0 },
581 { "Init9", &options
.init9
, NULL
, "", 0 },
582 { "Phone", &options
.phnum
, NULL
, "", 0 },
583 { "Phone1", &options
.phnum1
, NULL
, "", 0 },
584 { "Phone2", &options
.phnum2
, NULL
, "", 0 },
585 { "Phone3", &options
.phnum3
, NULL
, "", 0 },
586 { "Phone4", &options
.phnum4
, NULL
, "", 0 },
587 { "Dial Prefix", &options
.dial_prefix
, NULL
, "", 0 },
588 { "Area Code", &options
.areacode
, NULL
, "", 0 },
589 { "Dial Command", &options
.dial_cmd
, NULL
, "ATDT", 0 },
590 { "Username", &options
.login
, NULL
, "", 0 },
591 { "Login Prompt", &options
.login_prompt
, NULL
, "", 0 },
592 { "Password", &options
.password
, NULL
, "", 0 },
593 { "Password Prompt", &options
.pass_prompt
, NULL
, "", 0 },
594 { "PPPD Path", &options
.where_pppd
, NULL
, "/usr/sbin/pppd", 0 },
595 { "PPPD Option", &options
.pppd_option
, NULL
, "", 0 },
596 { "Force Address", &options
.force_addr
, NULL
, "", 0 },
597 { "Remote Name", &options
.remote
, NULL
, "*", 0 },
598 { "Default Reply", &options
.default_reply
,NULL
, "ppp", 0 },
599 { "Country", &options
.country
, NULL
, "", 0 },
600 { "Provider", &options
.provider
, NULL
, "", 0 },
601 { "Product", &options
.product
, NULL
, "", 0 },
602 { "Homepage", &options
.homepage
, NULL
, "", 0 },
603 { "DialMessage1", &options
.dialmessage1
, NULL
, "", 0 },
604 { "DialMessage2", &options
.dialmessage2
, NULL
, "", 0 },
605 { "DNS Test1", &options
.dnstest1
, NULL
, "www.suse.de", 0 },
606 { "DNS Test2", &options
.dnstest2
, NULL
, "www.suse.com", 0 },
609 { "Baud", NULL
, &options
.baud
, "", DEFAULT_BAUD
},
610 { "Carrier Check", NULL
, &options
.carrier_check
, "", true },
611 { "Stupid Mode", NULL
, &options
.stupid_mode
, "", false },
612 { "New PPPD", NULL
, &options
.new_pppd
, "", true },
613 { "Auto Reconnect", NULL
, &options
.auto_reconnect
,"", true },
614 { "Dial Attempts", NULL
, &options
.dial_attempts
, "", 0 },
615 { "Abort on Busy", NULL
, &options
.abort_on_busy
, "", false },
616 { "Abort on No Dialtone", NULL
, &options
.abort_on_no_dialtone
, "", true },
617 { "Compuserve", NULL
, &options
.compuserve
, "", false },
618 { "Tonline", NULL
, &options
.tonline
, "", false },
619 { "Auto DNS", NULL
, &options
.auto_dns
, "", true },
620 { "Check DNS", NULL
, &options
.check_dns
, "", true },
621 { "Check Def Route", NULL
, &options
.check_dfr
, "", true },
622 { "Idle Seconds", NULL
, &options
.idle_seconds
, "", 0 },
623 { "ISDN", NULL
, &options
.isdn
, "", false },
624 { "Ask Password", NULL
, &options
.ask_password
, "", false },
626 { NULL
, NULL
, NULL
, "", 0 }
629 char * d
= "Dialer Defaults";
631 for( int i
=0; opts
[i
].name
!= NULL
; i
++ )
633 if( opts
[i
].str_member
== NULL
)
635 // it's an int/bool option.
636 *( opts
[i
].int_member
) =
637 cfg
.fuzzy_getint( *sect_list
, opts
[i
].name
,
638 cfg
.getint( d
, opts
[i
].name
, opts
[i
].int_default
) );
642 // it's a string option.
643 *( opts
[i
].str_member
) =
644 cfg
.fuzzy_get( *sect_list
, opts
[i
].name
,
645 cfg
.get( d
, opts
[i
].name
, opts
[i
].str_default
) );
649 // Support Init, as well as Init1, to make old WvDial people happy.
650 const char * newopt
= cfg
.fuzzy_get( *sect_list
, "Init",
651 cfg
.get( d
, "Init", NULL
) );
653 options
.init1
= newopt
;
656 bool WvDialer::init_modem()
657 /*************************/
665 err( "Configuration does not specify a valid modem device.\n" );
667 return( false ); // if we get this error, we already have a problem.
670 for (count
= 0; count
< 3; count
++)
672 // the buffer is empty.
680 int flags
= fcntl( STDIN_FILENO
, F_GETFL
);
681 if( ( flags
& O_ACCMODE
) == O_RDWR
)
683 cloned
= modem
= new WvModemBase( STDIN_FILENO
);
687 // The following is needed for diald.
688 // Stdin is not opened for read/write.
689 ::close( STDIN_FILENO
);
690 if( getenv( "MODEM" ) == NULL
)
692 err( "stdin not read/write and $MODEM not set\n" );
695 // Try to open device $MODEM.
698 int tty
= ::open( getenv( "MODEM" ), flags
);
701 err( "can't open %s: %m\n", getenv( "MODEM" ) );
704 cloned
= modem
= new WvModemBase( tty
);
709 cloned
= modem
= new WvModem( options
.modem
, options
.baud
);
713 err( "Cannot open %s: %s\n", options
.modem
, modem
->errstr() );
717 log( "Initializing modem.\n" );
720 modem
->print( "\r\r\r\r\r" );
721 while( modem
->select( 100, true, false ) )
724 // Send up to nine init strings, in order.
726 for( init_count
=1; init_count
<=9; init_count
++ )
731 case 1: this_str
= &options
.init1
; break;
732 case 2: this_str
= &options
.init2
; break;
733 case 3: this_str
= &options
.init3
; break;
734 case 4: this_str
= &options
.init4
; break;
735 case 5: this_str
= &options
.init5
; break;
736 case 6: this_str
= &options
.init6
; break;
737 case 7: this_str
= &options
.init7
; break;
738 case 8: this_str
= &options
.init8
; break;
741 this_str
= &options
.init9
; break;
745 modem
->print( "%s\r", *this_str
);
746 log( "Sending: %s\n", *this_str
);
748 received
= wait_for_modem( init_responses
, 5000, true );
752 modem
->print( "ATQ0\r" );
753 log( "Sending: ATQ0\n" );
754 received
= wait_for_modem( init_responses
, 500, true );
755 modem
->print( "%s\r", *this_str
);
756 log( "Re-Sending: %s\n", *this_str
);
757 received
= wait_for_modem( init_responses
, 5000, true );
761 err( "Modem not responding.\n" );
764 err( "Bad init string.\n" );
769 err( "Bad init string.\n" );
775 // Everything worked fine.
776 log( "Modem initialized.\n" );
779 // allows us to exit the internal loop
784 // we tried 3 times and it didn't work.
789 void WvDialer::del_modem()
791 assert(cloned
== modem
);
802 WvModemBase
*WvDialer::take_modem()
810 cloned
= modem
= NULL
;
816 void WvDialer::give_modem(WvModemBase
*_modem
)
819 cloned
= modem
= _modem
;
823 void WvDialer::async_dial()
824 /*************************/
828 if( stat
== PreDial2
)
830 // Wait for three seconds and then go to PreDial1.
831 continue_select(3000);
836 if( stat
== PreDial1
)
838 // Hit enter a few times.
839 for( int i
=0; i
<3; i
++ )
841 modem
->write( "\r", 1 );
842 continue_select(500);
843 if (!isok() || !modem
)
852 // Construct the dial string. We use the dial command, prefix,
853 // area code, and phone number as specified in the config file.
855 switch( phnum_count
)
858 this_str
= &options
.phnum
;
861 this_str
= &options
.phnum1
;
864 this_str
= &options
.phnum2
;
867 this_str
= &options
.phnum3
;
871 this_str
= &options
.phnum4
;
875 WvString
s( "%s%s%s%s%s\r", options
.dial_cmd
,
877 !options
.dial_prefix
? "" : ",",
881 log( "Sending: %s\n", s
);
882 log( "Waiting for carrier.\n" );
887 received
= async_wait_for_modem( dial_responses
, true );
891 case -1: // nothing -- return control.
892 if( time( NULL
) - last_rx
>= 60 )
894 log( WvLog::Warning
, "Timed out while dialing. Trying again.\n" );
899 //if Attempts in wvdial.conf is 0..dont do anything
900 if(options
.dial_attempts
!= 0)
902 if(check_attempts_exceeded(connect_attempts
))
915 if( options.ask_password )
917 err( "Error: dial on demand does not work with option Ask Password = 1\n" );
921 if( getuid() != 0 ) {
922 err( "Hint: if authentication does not work, start wvdial.dod once as user root\n" );
926 papchap.put_secret( options.login, options.password, options.remote );
927 if( getuid() == 0 ) {
928 if( papchap.isok_pap() == false ) {
929 err("Warning: Could not modify %s: %s\n",PAP_SECRETS, strerror(errno));
931 if( papchap.isok_chap() == false ) {
932 err("Warning: Could not modify %s: %s\n",CHAP_SECRETS, strerror(errno));
938 if( options
.stupid_mode
== true || options
.tonline
== true )
942 log( "Carrier detected. Chatmode finished.\n" );
947 log( "Carrier detected. Starting PPP immediately.\n" );
953 log( "Carrier detected. Waiting for prompt.\n" );
957 case 1: // NO CARRIER
958 log( WvLog::Warning
, "No Carrier! Trying again.\n" );
962 continue_select(2000);
964 //if Attempts in wvdial.conf is 0..dont do anything
965 if(options
.dial_attempts
!= 0)
967 if(check_attempts_exceeded(connect_attempts
))
974 case 2: // NO DIALTONE
975 case 3: // NO DIAL TONE
976 if( options
.abort_on_no_dialtone
== true )
978 err( "No dial tone.\n" );
983 log( "No dial tone. Trying again in 5 seconds.\n" );
987 //if Attempts in wvdial.conf is 0..dont do anything
988 if(options
.dial_attempts
!= 0)
990 if(check_attempts_exceeded(connect_attempts
))
998 if( options
.abort_on_busy
== true )
1000 err( "The line is busy.\n" );
1005 if( phnum_count
++ == phnum_max
)
1007 if( phnum_count
== 0 )
1008 log( WvLog::Warning
, "The line is busy. Trying again.\n" );
1010 log( WvLog::Warning
, "The line is busy. Trying other number.\n");
1014 continue_select(2000);
1018 err( "Invalid dial command.\n" );
1020 //if Attempts in wvdial.conf is 0..dont do anything
1021 if(options
.dial_attempts
!= 0)
1023 if(check_attempts_exceeded(connect_attempts
))
1030 log( "Voice line detected. Trying again.\n" );
1035 //if Attempts in wvdial.conf is 0..dont do anything
1036 if(options
.dial_attempts
!= 0)
1038 if(check_attempts_exceeded(connect_attempts
))
1046 log( "Fax line detected. Trying again.\n" );
1050 if(options
.dial_attempts
!= 0)
1052 if(check_attempts_exceeded(connect_attempts
))
1059 case 8: // NO ANSWER
1060 log( WvLog::Warning
, "No Answer. Trying again.\n" );
1064 if(options
.dial_attempts
!= 0)
1066 if(check_attempts_exceeded(connect_attempts
))
1071 continue_select(2000);
1074 err( "Unknown dial response string.\n" );
1081 bool WvDialer::check_attempts_exceeded(int no_of_attempts
)
1082 /********************************************************/
1084 if(no_of_attempts
> options
.dial_attempts
)
1086 log( WvLog::Warning
, "Maximum Attempts Exceeded..Aborting!!\n" );
1096 static int set_echo( int desc
, int value
)
1097 /****************************************/
1099 struct termios settings
;
1101 if( isatty( desc
) != 1 )
1104 if( tcgetattr (desc
, &settings
) < 0 )
1106 perror ("error in tcgetattr");
1111 settings
.c_lflag
|= ECHO
;
1113 settings
.c_lflag
&= ~ECHO
;
1115 if( tcsetattr (desc
, TCSANOW
, &settings
) < 0 )
1117 perror ("error in tcgetattr");
1125 int WvDialer::ask_password()
1126 /**************************/
1130 log("Please enter password (or empty password to stop):\n" );
1131 // fflush( stdout ); // kinternet needs this - WvLog should do it
1134 set_echo( STDOUT_FILENO
, 0 );
1135 fgets( tmp
, 50, stdin
);
1136 set_echo( STDOUT_FILENO
, 1 );
1138 if( tmp
[ strlen(tmp
)-1 ] == '\n' )
1139 tmp
[ strlen(tmp
)-1 ] = '\0';
1141 options
.password
= tmp
;
1147 void WvDialer::start_ppp()
1148 /************************/
1150 if( chat_mode
) exit(0); // pppd is already started...
1152 WvString
addr_colon( "%s:", options
.force_addr
);
1153 WvString
speed( options
.baud
);
1154 WvString
idle_seconds( options
.idle_seconds
);
1156 const char *dev_str
= (const char *)options
.modem
;
1157 if (!(strncmp(options
.modem
, "/dev/", 5)))
1161 // open a pipe to access the messages of pppd
1162 if( pipe( pppd_msgfd
) == -1 )
1164 err("pipe failed: %s\n", strerror(errno
) );
1165 exit( EXIT_FAILURE
);
1167 pppd_log
= new WvFDStream( pppd_msgfd
[0] );
1168 WvString
buffer1("%s", pppd_msgfd
[1] );
1171 // open a pipe to pass password to pppd
1173 if (!options
.password
)
1175 if( pipe( pppd_passwdfd
) == -1 )
1177 err("pipe failed: %s\n", strerror(errno
) );
1178 exit( EXIT_FAILURE
);
1180 ::write( pppd_passwdfd
[1], (const char *) options
.password
, options
.password
.len() );
1181 ::close( pppd_passwdfd
[1] );
1182 buffer2
.append("%s", pppd_passwdfd
[0] );
1185 char const *argv_raw
[] = {
1193 "user", options
.login
,
1194 (!!options
.force_addr
) ? (const char *)addr_colon
: "noipdefault",
1195 options
.new_pppd
? "call" : NULL
,
1196 options
.new_pppd
? "wvdial" : NULL
,
1197 options
.new_pppd
&& options
.auto_dns
? "usepeerdns" : NULL
,
1198 options
.new_pppd
&& options
.isdn
? "default-asyncmap" : NULL
,
1199 options
.new_pppd
&& (!!options
.pppd_option
) ? (const char *) options
.pppd_option
: NULL
,
1200 options
.new_pppd
&& options
.idle_seconds
>= 0 ? "idle" : NULL
,
1201 options
.new_pppd
&& options
.idle_seconds
>= 0 ? (const char *)idle_seconds
: NULL
,
1203 // !!buffer2 ? "passwordfd" : NULL, !!buffer2 ? (const char *)buffer2 : NULL,
1207 /* Filter out NULL holes in the raw argv list: */
1208 char * argv
[sizeof(argv_raw
)];
1210 for (unsigned int i
= 0; i
< sizeof(argv_raw
)/sizeof(char *); i
++)
1213 argv
[argv_index
++] = (char *)argv_raw
[i
];
1215 argv
[argv_index
] = NULL
;
1217 if( access( options
.where_pppd
, X_OK
) != 0 )
1219 err( "Unable to run %s.\n", options
.where_pppd
);
1220 err( "Check permissions, or specify a \"PPPD Path\" option "
1221 "in wvdial.conf.\n" );
1226 if (options
.dialmessage1
.len() || options
.dialmessage2
.len())
1228 log( WvLog::Notice
, "\
1229 ==========================================================================\n");
1230 log( WvLog::Notice
, "> %s\n", options
.dialmessage1
);
1231 if (options
.dialmessage2
.len())
1232 log( WvLog::Notice
, "> %s\n", options
.dialmessage2
);
1233 log( WvLog::Notice
, "\
1234 ==========================================================================\n");
1235 if (options
.homepage
.len())
1236 log( WvLog::Notice
, "Homepage of %s: %s\n",
1237 options
.provider
.len() ? (const char *)options
.provider
: "this provider",
1243 log( WvLog::Notice
, "Starting pppd at %s", ctime( &now
) );
1245 // PP - Put this back in, since we're not using passwordfd unless we're
1246 // SuSE... how did this work without this?
1248 papchap
.put_secret( options
.login
, options
.password
, options
.remote
);
1249 if( papchap
.isok_pap() == false )
1251 err( "Warning: Could not modify %s: %s\n"
1252 "--> PAP (Password Authentication Protocol) may be flaky.\n",
1253 PAP_SECRETS
, strerror( errno
) );
1255 if( papchap
.isok_chap() == false )
1257 err( "Warning: Could not modify %s: %s\n"
1258 "--> CHAP (Challenge Handshake) may be flaky.\n",
1259 CHAP_SECRETS
, strerror( errno
) );
1262 ppp_pipe
= new WvPipe( argv
[0], argv
, false, false, false,
1263 modem
, modem
, modem
);
1265 log( WvLog::Notice
, "Pid of pppd: %s\n", ppp_pipe
->getpid() );
1269 connected_at
= time( NULL
);
1272 void WvDialer::async_waitprompt()
1273 /*******************************/
1276 const char *prompt_response
;
1278 if( options
.carrier_check
== true )
1280 if( !modem
|| !modem
->carrier() )
1282 err( "Connected, but carrier signal lost! Retrying...\n" );
1288 received
= async_wait_for_modem( prompt_strings
, false, true );
1291 // We have a PPP sequence!
1292 log( "PPP negotiation detected.\n" );
1295 else if( received
== -1 )
1297 // some milliseconds must have passed without receiving anything,
1298 // or async_wait_for_modem() would not have returned yet.
1300 // check to see if we are at a prompt.
1301 // Note: the buffer has been lowered by strlwr() already.
1303 prompt_response
= brain
->check_prompt( buffer
);
1304 if( prompt_response
!= NULL
)
1305 modem
->print( "%s\r", prompt_response
);
1310 static void strip_parity( char * buf
, size_t size
)
1311 /*************************************************/
1312 // clear the parity bit on incoming data (to allow 7e1 connections)
1322 int WvDialer::wait_for_modem( char * strs
[],
1326 /***********************************************/
1332 const char *ppp_marker
= NULL
;
1334 while( modem
->select( timeout
, true, false ) )
1336 last_rx
= time( NULL
);
1338 offset
+= modem
->read( buffer
+ offset
, INBUF_SIZE
- offset
);
1340 // make sure we do not split lines TOO arbitrarily, or the
1341 // logs will look bad.
1342 while( offset
< INBUF_SIZE
&& modem
->select( 100, true, false ) )
1343 offset
+= modem
->read( buffer
+ offset
, INBUF_SIZE
- offset
);
1345 // Make sure there is a NULL on the end of the buffer.
1346 buffer
[ offset
] = '\0';
1348 // Now turn all the NULLs in the middle of the buffer to spaces, for
1350 replace_char( buffer
+ onset
, '\0', ' ', offset
- onset
);
1351 strip_parity( buffer
+ onset
, offset
- onset
);
1352 replace_char( buffer
+ onset
, '\0', ' ', offset
- onset
);
1355 modemrx
.write( buffer
+ onset
, offset
- onset
);
1357 strlwr( buffer
+ onset
);
1359 // Now we can search using strstr.
1360 for( result
= 0; strs
[ result
] != NULL
; result
++ )
1362 len
= strlen( strs
[ result
] );
1363 soff
= strstr( buffer
, strs
[ result
] );
1365 if( soff
&& ( !neednewline
1366 || strchr( soff
, '\n' ) || strchr( soff
, '\r' ) ) )
1368 memmove( buffer
, soff
+ len
,
1369 offset
- (int)( soff
+len
- buffer
) );
1370 offset
-= (int)( soff
+len
- buffer
);
1375 if( strs
[ result
] == NULL
)
1378 // Search the buffer for a valid menu option...
1379 // If guess_menu returns an offset, we zap everything before it in
1380 // the buffer. This prevents finding the same menu option twice.
1381 ppp_marker
= brain
->guess_menu( buffer
);
1382 if (strs
!= dial_responses
)
1384 if( ppp_marker
!= NULL
)
1385 memset( buffer
, ' ', ppp_marker
-buffer
);
1388 // Looks like we didn't find anything. Is the buffer full yet?
1389 if( offset
== INBUF_SIZE
)
1391 // yes, move the second half to the first half for next time.
1392 memmove( buffer
, buffer
+ INBUF_SIZE
/2,
1393 INBUF_SIZE
- INBUF_SIZE
/2 );
1394 offset
= INBUF_SIZE
/2;
1401 buffer
[ offset
] = 0;
1402 return( result
); // -1 == timeout
1405 int WvDialer::async_wait_for_modem( char * strs
[], bool neednl
, bool verbose
)
1406 /****************************************************************************/
1408 return( wait_for_modem( strs
, 10, neednl
, verbose
) );
1411 void WvDialer::reset_offset()
1412 /***************************/