2 * kPPP: A pppd Front End for the KDE project
6 * Copyright (C) 1997 Bernd Johannes Wuebben
7 * wuebben@math.cornell.edu
9 * This file was added by Harri Porten <porten@tu-harburg.de>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free
24 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 #include <sys/ioctl.h>
39 #include "requester.h"
42 #include <kcmdlineargs.h>
43 #include <config-kppp.h>
45 static sigjmp_buf jmp_buffer
;
47 Modem
*Modem::modem
= 0;
53 modem_is_locked(false)
57 args
= KCmdLineArgs::parsedArgs();
66 speed_t
Modem::modemspeed() {
67 // convert the string modem speed int the gpppdata object to a t_speed type
68 // to set the modem. The constants here should all be ifdef'd because
69 // other systems may not have them
70 int i
= gpppdata
.speed().toInt()/100;
116 bool Modem::opentty() {
119 if (args
->isSet("dev"))
120 device
= args
->getOption("dev");
122 device
= gpppdata
.modemDevice();
123 kDebug() << "Opening Device: " << device
;
125 if((modemfd
= Requester::rq
->openModem(device
))<0) {
126 errmsg
= i18n("Unable to open modem.");
131 if(gpppdata
.UseCDLine()) {
132 if(ioctl(modemfd
, TIOCMGET
, &flags
) == -1) {
133 errmsg
= i18n("Unable to detect state of CD line.");
138 if ((flags
&TIOCM_CD
) == 0) {
139 errmsg
= i18n("The modem is not ready.");
148 tcflush (modemfd
, TCIOFLUSH
);
150 if(tcgetattr(modemfd
, &tty
) < 0){
151 // this helps in some cases
152 tcsendbreak(modemfd
, 0);
154 if(tcgetattr(modemfd
, &tty
) < 0){
155 errmsg
= i18n("The modem is busy.");
162 memset(&initial_tty
,'\0',sizeof(initial_tty
));
166 tty
.c_cc
[VMIN
] = 0; // nonblocking
171 tty
.c_cflag
&= ~(CSIZE
| CSTOPB
| PARENB
);
172 tty
.c_cflag
|= CS8
| CREAD
;
173 tty
.c_cflag
|= CLOCAL
; // ignore modem status lines
174 tty
.c_iflag
= IGNBRK
| IGNPAR
/* | ISTRIP */ ;
175 tty
.c_lflag
&= ~ICANON
; // non-canonical mode
176 tty
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHOKE
);
179 // the english/i18n mix below is ugly but we want to keep working
180 // after someone changed the code to use i18n'ed config values
181 QString flowCtrl
= gpppdata
.flowcontrol();
182 if(flowCtrl
!= "None" && flowCtrl
!= i18n("None")) {
183 if(flowCtrl
== "CRTSCTS" || flowCtrl
== i18n("Hardware [CRTSCTS]")) {
184 tty
.c_cflag
|= CRTSCTS
;
187 tty
.c_iflag
|= IXON
| IXOFF
;
188 tty
.c_cc
[VSTOP
] = 0x13; /* DC3 = XOFF = ^S */
189 tty
.c_cc
[VSTART
] = 0x11; /* DC1 = XON = ^Q */
193 tty
.c_cflag
&= ~CRTSCTS
;
194 tty
.c_iflag
&= ~(IXON
| IXOFF
);
197 cfsetospeed(&tty
, modemspeed());
198 cfsetispeed(&tty
, modemspeed());
202 if(tcsetattr(modemfd
, TCSANOW
, &tty
) < 0){
203 errmsg
= i18n("The modem is busy.");
209 errmsg
= i18n("Modem Ready.");
214 bool Modem::closetty() {
217 /* discard data not read or transmitted */
218 tcflush(modemfd
, TCIOFLUSH
);
220 if(tcsetattr(modemfd
, TCSANOW
, &initial_tty
) < 0){
221 errmsg
= i18n("Can not restore tty settings: tcsetattr()\n");
234 void Modem::readtty(int) {
239 // read data in chunks of up to 200 bytes
240 if((len
= ::read(modemfd
, buffer
, 200)) > 0) {
241 // split buffer into single characters for further processing
242 for(int i
= 0; i
< len
; i
++) {
243 c
= buffer
[i
] & 0x7F;
250 void Modem::notify(const QObject
*receiver
, const char *member
) {
251 connect(this, SIGNAL(charWaiting(unsigned char)), receiver
, member
);
257 disconnect(SIGNAL(charWaiting(unsigned char)));
262 void Modem::startNotifier() {
265 sn
= new QSocketNotifier(modemfd
, QSocketNotifier::Read
, this);
266 connect(sn
, SIGNAL(activated(int)), SLOT(readtty(int)));
267 kDebug(5002) << "QSocketNotifier started!";
269 // Debug("QSocketNotifier re-enabled!");
270 sn
->setEnabled(true);
276 void Modem::stopNotifier() {
278 sn
->setEnabled(false);
282 kDebug(5002) << "QSocketNotifier stopped!";
287 void Modem::flush() {
289 while(read(modemfd
, &c
, 1) == 1);
293 bool Modem::writeChar(unsigned char c
) {
296 s
= write(modemfd
, &c
, 1);
298 kError(5002) << "write() in Modem::writeChar failed" << endl
;
307 bool Modem::writeLine(const char *buf
) {
308 int len
= strlen(buf
);
309 char *b
= new char[len
+2];
311 // different modems seem to need different line terminations
312 QString term
= gpppdata
.enter();
315 else if(term
== "CR")
317 else if(term
== "CR/LF") {
323 int wr
= write(modemfd
, &b
[len
-l
], l
);
327 // TODO do something meaningful with the error code (or ignore it
328 kError(5002) << "write() in Modem::writeLine failed" << endl
;
339 bool Modem::hangup() {
340 // this should really get the modem to hang up and go into command mode
341 // If anyone sees a fault in the following please let me know, since
342 // this is probably the most imporant snippet of code in the whole of
343 // kppp. If people complain about kppp being stuck, this piece of code
344 // is most likely the reason.
345 struct termios temptty
;
349 // is this Escape & HangupStr stuff really necessary ? (Harri)
351 if (data_mode
) escape_to_command_mode();
353 // Then hangup command
354 writeLine(gpppdata
.modemHangupStr().toLocal8Bit());
356 usleep(gpppdata
.modemInitDelay() * 10000); // 0.01 - 3.0 sec
358 #ifndef DEBUG_WO_DIALING
359 if (sigsetjmp(jmp_buffer
, 1) == 0) {
360 // set alarm in case tcsendbreak() hangs
361 signal(SIGALRM
, alarm_handler
);
364 tcsendbreak(modemfd
, 0);
367 signal(SIGALRM
, SIG_IGN
);
369 // we reach this point if the alarm handler got called
373 errmsg
= i18n("The modem does not respond.");
377 #ifndef __svr4__ // drops DTR but doesn't set it afterwards again. not good for init.
378 tcgetattr(modemfd
, &temptty
);
379 cfsetospeed(&temptty
, B0
);
380 cfsetispeed(&temptty
, B0
);
381 tcsetattr(modemfd
, TCSAFLUSH
, &temptty
);
384 ioctl(modemfd
, TIOCMGET
, &modemstat
);
385 modemstat
&= ~TIOCM_DTR
;
386 ioctl(modemfd
, TIOCMSET
, &modemstat
);
387 ioctl(modemfd
, TIOCMGET
, &modemstat
);
388 modemstat
|= TIOCM_DTR
;
389 ioctl(modemfd
, TIOCMSET
, &modemstat
);
392 usleep(gpppdata
.modemInitDelay() * 10000); // 0.01 - 3.0 secs
394 cfsetospeed(&temptty
, modemspeed());
395 cfsetispeed(&temptty
, modemspeed());
396 tcsetattr(modemfd
, TCSAFLUSH
, &temptty
);
404 void Modem::escape_to_command_mode() {
405 // Send Properly bracketed escape code to put the modem back into command state.
406 // A modem will accept AT commands only when it is in command state.
407 // When a modem sends the host the CONNECT string, that signals
408 // that the modem is now in the connect state (no long accepts AT commands.)
409 // Need to send properly timed escape sequence to put modem in command state.
410 // Escape codes and guard times are controlled by S2 and S12 values.
412 tcflush(modemfd
, TCIOFLUSH
);
414 // +3 because quiet time must be greater than guard time.
415 usleep((gpppdata
.modemEscapeGuardTime()+3)*20000);
416 QByteArray tmp
= gpppdata
.modemEscapeStr().toLocal8Bit();
417 write(modemfd
, tmp
.data(), tmp
.length());
418 tcflush(modemfd
, TCIOFLUSH
);
419 usleep((gpppdata
.modemEscapeGuardTime()+3)*20000);
425 const QString
Modem::modemMessage() {
430 QString
Modem::parseModemSpeed(const QString
&s
) {
431 // this is a small (and bad) parser for modem speeds
437 kDebug(5002) << "Modem reported result string: " << s
;
441 QRegExp rrx
[RXMAX
] = {
442 QRegExp("[0-9]+[:/ ]RX", Qt::CaseInsensitive
),
443 QRegExp("[0-9]+RX", Qt::CaseInsensitive
),
444 QRegExp("[/: -][0-9]+[/: ]", Qt::CaseInsensitive
),
445 QRegExp("[/: -][0-9]+$", Qt::CaseInsensitive
),
446 QRegExp("CARRIER [^0-9]*[0-9]+", Qt::CaseInsensitive
),
447 QRegExp("CONNECT [^0-9]*[0-9]+", Qt::CaseInsensitive
),
448 QRegExp("[0-9]+") // panic mode
451 QRegExp trx
[TXMAX
] = {
452 QRegExp("[0-9]+[:/ ]TX", Qt::CaseInsensitive
),
453 QRegExp("[0-9]+TX", Qt::CaseInsensitive
)
456 for(i
= 0; i
< RXMAX
; i
++) {
457 int len
, idx
, result
;
458 if((idx
= rrx
[i
].indexIn(s
)) > -1) {
459 len
= rrx
[i
].matchedLength();
462 // rrx[i] has been matched, idx contains the start of the match
463 // and len contains how long the match is. Extract the match.
465 QString sub
= s
.mid(idx
, len
);
468 // Now extract the digits only from the match, which will
469 // then be converted to an int.
471 if ((idx
= rrx
[RXMAX
-1].indexIn( sub
)) > -1) {
472 len
= rrx
[RXMAX
-1].matchedLength();
473 sub
= sub
.mid(idx
, len
);
474 result
= sub
.toInt();
483 for(i
= 0; i
< TXMAX
; i
++) {
484 int len
, idx
, result
;
485 if((idx
= trx
[i
].indexIn(s
)) > -1) {
486 len
= trx
[i
].matchedLength();
489 // trx[i] has been matched, idx contains the start of the match
490 // and len contains how long the match is. Extract the match.
492 QString sub
= s
.mid(idx
, len
);
495 // Now extract the digits only from the match, which will then
496 // be converted to an int.
498 if((idx
= rrx
[RXMAX
-1].indexIn(sub
)) > -1) {
499 len
= rrx
[RXMAX
-1].matchedLength();
500 sub
= sub
.mid(idx
, len
);
501 result
= sub
.toInt();
510 if(rx
== -1 && tx
== -1)
511 result
= i18n("Unknown speed");
514 else if(rx
== -1) // should not happen
517 result
.sprintf("%d/%d", rx
, tx
);
519 kDebug(5002) << "The parsed result is: " << result
;
525 // Lock modem device. Returns 0 on success 1 if the modem is locked and -1 if
526 // a lock file can't be created ( permission problem )
527 int Modem::lockdevice() {
529 char newlock
[80]=""; // safe
531 if(!gpppdata
.modemLockFile()) {
532 kDebug(5002) << "The user doesn't want a lockfile.";
540 if (args
->isSet("dev"))
541 device
= args
->getOption("dev");
543 device
= gpppdata
.modemDevice();
545 QString lockfile
= LOCK_DIR
"/LCK..";
546 lockfile
+= device
.mid(5); // append everything after /dev/
548 if(access(QFile::encodeName(lockfile
), F_OK
) == 0) {
549 if ((fd
= Requester::rq
->openLockfile(QFile::encodeName(lockfile
), O_RDONLY
)) >= 0) {
550 // Mario: it's not necessary to read more than lets say 32 bytes. If
551 // file has more than 32 bytes, skip the rest
552 char oldlock
[33]; // safe
553 int sz
= read(fd
, &oldlock
, 32);
559 kDebug(5002) << "Device is locked by: " << &oldlock
;
562 int match
= sscanf(oldlock
, "%d", &oldpid
);
564 // found a pid in lockfile ?
565 if (match
< 1 || oldpid
<= 0)
568 // check if process exists
569 if (kill((pid_t
)oldpid
, 0) == 0 || errno
!= ESRCH
)
572 kDebug(5002) << "lockfile is stale";
576 fd
= Requester::rq
->openLockfile(device
,
577 O_WRONLY
|O_TRUNC
|O_CREAT
);
579 sprintf(newlock
,"%010d\n", getpid());
580 kDebug(5002) << "Locking Device: " << newlock
;
582 write(fd
, newlock
, strlen(newlock
));
584 modem_is_locked
=true;
594 // UnLock modem device
595 void Modem::unlockdevice() {
596 if (modem_is_locked
) {
597 kDebug(5002) << "UnLocking Modem Device";
598 Requester::rq
->removeLockfile();
599 modem_is_locked
=false;
603 void alarm_handler(int) {
604 // fprintf(stderr, "alarm_handler(): Received SIGALRM\n");
607 siglongjmp(jmp_buffer
, 1);