2 * kPPP: A pppd front end for the KDE project
4 * Copyright (C) 1997 Bernd Johannes Wuebben
5 * wuebben@math.cornell.edu
6 * Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
9 * Copyright (C) 1997 Jay Painter
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free
23 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include <config-kppp.h>
33 #include <sys/socket.h>
40 #define __xnet_connect connect
43 #ifdef HAVE_SYS_PARAM_H
44 #include <sys/param.h>
47 #include <kaboutdata.h>
48 #include <kapplication.h>
49 #include <kcmdlineargs.h>
53 #include <kmessagebox.h>
54 #include <kstandarddirs.h>
56 #include "kpppwidget.h"
59 #include "providerdb.h"
61 #include "requester.h"
63 //#include <X11/Xlib.h>
67 static const char description
[] =
68 I18N_NOOP("A dialer and front-end to pppd");
73 // for testing purposes
76 // initial effective user id before possible suid status is dropped
78 // helper process' pid
81 QString local_ip_address
;
82 QString remote_ip_address
;
87 static int kppp_x_errhandler( Display
*dpy
, XErrorEvent
*err
) {
88 char errstr
[256]; // safe
91 if(gpppdata.pppdpid() >= 0) {
92 kill(gpppdata.pppdpid(), SIGTERM);
95 p_kppp->stopAccounting();
99 XGetErrorText( dpy
, err
->error_code
, errstr
, 256 );
100 kFatal() << "X Error: " << errstr
;
101 kFatal() << "Major opcode: " << err
->request_code
;
107 static int kppp_xio_errhandler( Display
* ) {
108 if(gpppdata
.get_xserver_exit_disconnect()) {
109 fprintf(stderr
, "X11 Error!\n");
110 if(gpppdata
.pppdRunning())
111 Requester::rq
->killPPPDaemon();
113 p_kppp
->stopAccounting();
115 Modem::modem
->unlockdevice();
118 kFatal() << "Fatal IO error: client killed";
126 int main( int argc
, char **argv
) {
127 // make sure that open/fopen and so on NEVER return 1 or 2 (stdout and stderr)
128 // Expl: if stdout/stderr were closed on program start (by parent), open()
129 // would return a FD of 1, 2 (or even 0 if stdin was closed too)
130 if(fcntl(0, F_GETFL
) == -1)
131 (void)open("/dev/null", O_RDONLY
);
133 if(fcntl(1, F_GETFL
) == -1)
134 (void)open("/dev/null", O_WRONLY
);
136 if(fcntl(2, F_GETFL
) == -1)
137 (void)open("/dev/null", O_WRONLY
);
139 // Don't insert anything above this line unless you really know what
140 // you're doing. We're most likely running setuid root here,
141 // until we drop this status a few lines below.
143 if(socketpair(AF_UNIX
, SOCK_DGRAM
, 0, sockets
) != 0) {
144 fprintf(stderr
, "error creating socketpair !\n");
148 switch(helperPid
= fork()) {
151 // make process leader of new group
155 signal(SIGHUP
, SIG_IGN
);
156 (void) new Opener(sockets
[1]);
157 // we should never get here
161 perror("fork() failed");
168 // drop setuid status
170 if (setgid(getgid()) < 0 && errno
!= EPERM
) {
171 perror("setgid() failed");
175 if (geteuid() != getuid()) {
176 perror("setuid() failed");
181 // end of setuid-dropping block.
184 // install exit handler that will kill the helper process
187 // not needed anymore, just causes problems with broken setup
188 // if(getHomeDir() != 0)
189 // setenv("HOME", getHomeDir(), 1);
191 (void) new Requester(sockets
[0]);
193 KAboutData
aboutData("kppp", 0, ki18n("KPPP"),
194 KPPPVERSION
, ki18n(description
), KAboutData::License_GPL
,
195 ki18n("(c) 1999-2002, The KPPP Developers"));
196 aboutData
.addAuthor(ki18n("Harri Porten"), ki18n("Current maintainer"), "porten@kde.org");
197 aboutData
.addAuthor(ki18n("Bernd Wuebben"), ki18n("Original author"), "wuebben@kde.org");
198 aboutData
.addAuthor(ki18n("Mario Weilguni"));
200 KCmdLineArgs::init( argc
, argv
, &aboutData
);
202 KCmdLineOptions options
;
203 options
.add("c <account_name>", ki18n("Connect using 'account_name'"));
204 options
.add("m <modem_name>", ki18n("Connect using 'modem_name'"));
205 options
.add("k", ki18n("Terminate an existing connection"));
206 options
.add("q", ki18n("Quit after end of connection"));
207 options
.add("r <rule_file>", ki18n("Check syntax of rule_file"));
208 options
.add("T", ki18n("Enable test-mode"));
209 options
.add("dev <device_name>", ki18n("Use the specified device"));
210 KCmdLineArgs::addCmdLineOptions( options
);
216 // set portable locale for decimal point
217 setlocale(LC_NUMERIC
,"C");
219 // open configuration file
222 kDebug(5002) << "helperPid: " << (int) helperPid
;
224 KCmdLineArgs
*args
= KCmdLineArgs::parsedArgs();
226 bool terminate_connection
= args
->isSet("k");
227 if (args
->isSet("r"))
228 return RuleSet::checkRuleFile(args
->getOption("r"));
230 TESTING
= args
->isSet("T");
232 // make sure that nobody can read the password from the
234 QString configFile
= KGlobal::dirs()->saveLocation("config")
235 + kapp
->objectName() + "rc";
236 if(access(QFile::encodeName(configFile
), F_OK
) == 0)
237 chmod(QFile::encodeName(configFile
), S_IRUSR
| S_IWUSR
);
239 // do we really need to generate an empty directory structure here ?
240 KGlobal::dirs()->saveLocation("appdata", "Rules");
242 int pid
= create_pidfile();
243 QString err_msg
= i18n("kppp can not create or read from\n%1.", pidfile
);
246 KMessageBox::error(0L, err_msg
);
250 if (terminate_connection
) {
263 gpppdata
.setAccountByIndex(0);
274 QString msg
= i18n("kppp has detected a %1 file.\n"
275 "Another instance of kppp seems to be "
276 "running under process-ID %2.\n"
277 "Please click Exit, make sure that you are "
278 "not running another kppp, delete the pid "
279 "file, and restart kppp.\n"
280 "Alternatively, if you have determined that "
281 "there is no other kppp running, please "
282 "click Continue to begin.",
284 int button
= KMessageBox::warningYesNo(0, msg
, i18n("Error"),
285 KGuiItem(i18n("Exit")), KStandardGuiItem::cont());
286 if (button
== KMessageBox::Yes
) /* exit */
290 pid
= create_pidfile();
292 KMessageBox::error(0L, err_msg
);
300 (void)new DockWidget(p_kppp
->con_win
, "dockw", p_kppp
->stats
);
302 // we really don't want to die accidentally, since that would leave the
303 // modem connected. If you really really want to kill me you must send
305 signal(SIGINT
, sighandler
);
306 signal(SIGCHLD
, sighandler
);
307 signal(SIGUSR1
, sighandler
);
308 signal(SIGTERM
, SIG_IGN
);
310 // XSetErrorHandler( kppp_x_errhandler );
311 // XSetIOErrorHandler( kppp_xio_errhandler );
321 pid_t
execute_command (const QString
& cmd
) {
322 QByteArray command
= QFile::encodeName(cmd
);
323 if (command
.isEmpty() || command
.length() > COMMAND_SIZE
)
328 kDebug(5002) << "Executing command: " << command
;
330 QApplication::flush();
331 if((id
= fork()) == 0) {
332 // don't bother dieppp()
333 signal(SIGCHLD
, SIG_IGN
);
335 // close file descriptors
336 const int open_max
= sysconf( _SC_OPEN_MAX
);
337 for (int fd
= 3; fd
< open_max
; ++fd
)
340 // drop privileges if running setuid root
352 // Create a file containing the current pid. Returns 0 on success,
353 // -1 on failure or the pid of an already running kppp process.
354 pid_t
create_pidfile() {
356 char pidstr
[40]; // safe
358 pidfile
= KGlobal::dirs()->saveLocation("appdata") + "kppp.pid";
360 if(access(QFile::encodeName(pidfile
), F_OK
) == 0) {
362 if((access(QFile::encodeName(pidfile
), R_OK
) < 0) ||
363 (fd
= open(QFile::encodeName(pidfile
), O_RDONLY
)) < 0)
366 int sz
= read(fd
, &pidstr
, 32);
372 kDebug(5002) << "found kppp.pid containing: " << pidstr
;
377 int match
= sscanf(pidstr
, "%d", &oldpid
);
379 // found a pid in pidfile ?
380 if (match
< 1 || oldpid
<= 0)
383 // check if process exists
384 if (kill((pid_t
)oldpid
, 0) == 0 || errno
!= ESRCH
)
388 kDebug(5002) << "pidfile is stale\n";
392 if((fd
= open(QFile::encodeName(pidfile
), O_WRONLY
| O_CREAT
| O_EXCL
,
393 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
)) == -1)
396 fchown(fd
, getuid(), getgid());
398 sprintf(pidstr
, "%d\n", getpid());
399 write(fd
, pidstr
, strlen(pidstr
));
406 bool remove_pidfile() {
409 // only remove regular files with user write permissions
410 if(stat(QFile::encodeName(pidfile
), &st
) == 0 )
411 if(S_ISREG(st
.st_mode
) && (access(QFile::encodeName(pidfile
), W_OK
) == 0)) {
412 unlink(QFile::encodeName(pidfile
));
416 fprintf(stderr
, "error removing pidfile.\n");
423 // don't bother about SIGCHLDs anymore
424 signal(SIGCHLD
, SIG_IGN
);
425 // fprintf(stderr, "myShutDown(%i)\n", status);
429 // fprintf(stderr, "killing child process %i", pid);
434 void sighandler(int sig
) {
438 if(id
>= 0 && id
== helperPid
) // helper process died
439 e
= new SignalEvent(sig
);
440 } else if(sig
== SIGINT
|| sig
== SIGUSR1
)
441 e
= new SignalEvent(sig
);
443 // let eventFilter() deal with this when we're back in the loop
445 QApplication::postEvent(p_kppp
, e
);
447 signal(sig
, sighandler
); // reinstall signal handler