You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
12 KiB
454 lines
12 KiB
/*
|
|
* kPPP: A pppd front end for the KDE project
|
|
*
|
|
* Copyright (C) 1997 Bernd Johannes Wuebben
|
|
* wuebben@math.cornell.edu
|
|
* Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
|
|
*
|
|
* based on EzPPP:
|
|
* Copyright (C) 1997 Jay Painter
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this program; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "main.h"
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <locale.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef _XPG4_2
|
|
#define __xnet_connect connect
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
#include <sys/param.h>
|
|
#endif
|
|
|
|
#include <kaboutdata.h>
|
|
#include <kapplication.h>
|
|
#include <kcmdlineargs.h>
|
|
#include <kdebug.h>
|
|
#include <kglobal.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
#include "kpppwidget.h"
|
|
#include "opener.h"
|
|
#include "pppdata.h"
|
|
#include "providerdb.h"
|
|
#include "version.h"
|
|
#include "requester.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
static const char description[] =
|
|
I18N_NOOP("A dialer and front-end to pppd");
|
|
|
|
static const KCmdLineOptions options[] =
|
|
{
|
|
{ "c <account_name>", I18N_NOOP("Connect using 'account_name'"), 0 },
|
|
{ "m <modem_name>", I18N_NOOP("Connect using 'modem_name'"), 0 },
|
|
{ "k", I18N_NOOP("Terminate an existing connection"), 0 },
|
|
{ "q", I18N_NOOP("Quit after end of connection"), 0 },
|
|
{ "r <rule_file>", I18N_NOOP("Check syntax of rule_file"), 0 },
|
|
{ "T", I18N_NOOP("Enable test-mode"), 0 },
|
|
{ "dev <device_name>", I18N_NOOP("Use the specified device"), 0 },
|
|
KCmdLineLastOption
|
|
};
|
|
|
|
|
|
KPPPWidget* p_kppp;
|
|
|
|
// for testing purposes
|
|
bool TESTING=0;
|
|
|
|
// initial effective user id before possible suid status is dropped
|
|
uid_t euid;
|
|
// helper process' pid
|
|
pid_t helperPid = -1;
|
|
|
|
TQString local_ip_address;
|
|
TQString remote_ip_address;
|
|
TQString pidfile;
|
|
|
|
#if 0
|
|
extern "C" {
|
|
static int kppp_x_errhandler( Display *dpy, XErrorEvent *err ) {
|
|
char errstr[256]; // safe
|
|
|
|
/*
|
|
if(gpppdata.pppdpid() >= 0) {
|
|
kill(gpppdata.pppdpid(), SIGTERM);
|
|
}
|
|
|
|
p_kppp->stopAccounting();
|
|
removedns();
|
|
unlockdevice();*/
|
|
|
|
XGetErrorText( dpy, err->error_code, errstr, 256 );
|
|
kdFatal() << "X Error: " << errstr << endl;
|
|
kdFatal() << "Major opcode: " << err->request_code << endl;
|
|
exit(256);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int kppp_xio_errhandler( Display * ) {
|
|
if(gpppdata.get_xserver_exit_disconnect()) {
|
|
fprintf(stderr, "X11 Error!\n");
|
|
if(gpppdata.pppdRunning())
|
|
Requester::rq->killPPPDaemon();
|
|
|
|
p_kppp->stopAccounting();
|
|
removedns();
|
|
Modem::modem->unlockdevice();
|
|
return 0;
|
|
} else{
|
|
kdFatal() << "Fatal IO error: client killed" << endl;
|
|
exit(256);
|
|
return 0;
|
|
}
|
|
}
|
|
} /* extern "C" */
|
|
#endif
|
|
|
|
int main( int argc, char **argv ) {
|
|
// make sure that open/fopen and so on NEVER return 1 or 2 (stdout and stderr)
|
|
// Expl: if stdout/stderr were closed on program start (by parent), open()
|
|
// would return a FD of 1, 2 (or even 0 if stdin was closed too)
|
|
if(fcntl(0, F_GETFL) == -1)
|
|
(void)open("/dev/null", O_RDONLY);
|
|
|
|
if(fcntl(1, F_GETFL) == -1)
|
|
(void)open("/dev/null", O_WRONLY);
|
|
|
|
if(fcntl(2, F_GETFL) == -1)
|
|
(void)open("/dev/null", O_WRONLY);
|
|
|
|
// Don't insert anything above this line unless you really know what
|
|
// you're doing. We're most likely running setuid root here,
|
|
// until we drop this status a few lines below.
|
|
int sockets[2];
|
|
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) != 0) {
|
|
fprintf(stderr, "error creating socketpair !\n");
|
|
return 1;
|
|
}
|
|
|
|
switch(helperPid = fork()) {
|
|
case 0:
|
|
// child process
|
|
// make process leader of new group
|
|
setsid();
|
|
umask(0);
|
|
close(sockets[0]);
|
|
signal(SIGHUP, SIG_IGN);
|
|
(void) new Opener(sockets[1]);
|
|
// we should never get here
|
|
_exit(1);
|
|
|
|
case -1:
|
|
perror("fork() failed");
|
|
exit(1);
|
|
}
|
|
|
|
// parent process
|
|
close(sockets[1]);
|
|
|
|
// drop setuid status
|
|
euid = geteuid();
|
|
if (setgid(getgid()) < 0 && errno != EPERM) {
|
|
perror("setgid() failed");
|
|
exit(1);
|
|
}
|
|
setuid(getuid());
|
|
if (geteuid() != getuid()) {
|
|
perror("setuid() failed");
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// end of setuid-dropping block.
|
|
//
|
|
|
|
// install exit handler that will kill the helper process
|
|
atexit(myShutDown);
|
|
|
|
// not needed anymore, just causes problems with broken setup
|
|
// if(getHomeDir() != 0)
|
|
// setenv("HOME", getHomeDir(), 1);
|
|
|
|
(void) new Requester(sockets[0]);
|
|
|
|
KAboutData aboutData("kppp", I18N_NOOP("KPPP"),
|
|
KPPPVERSION, description, KAboutData::License_GPL,
|
|
I18N_NOOP("(c) 1999-2002, The KPPP Developers"));
|
|
aboutData.addAuthor("Harri Porten", I18N_NOOP("Current maintainer"), "porten@kde.org");
|
|
aboutData.addAuthor("Bernd Wuebben", I18N_NOOP("Original author"), "wuebben@kde.org");
|
|
aboutData.addAuthor("Mario Weilguni",0, "");
|
|
|
|
TDECmdLineArgs::init( argc, argv, &aboutData );
|
|
TDECmdLineArgs::addCmdLineOptions( options );
|
|
|
|
|
|
|
|
TDEApplication a;
|
|
|
|
// set portable locale for decimal point
|
|
setlocale(LC_NUMERIC ,"C");
|
|
|
|
// open configuration file
|
|
gpppdata.open();
|
|
|
|
kdDebug(5002) << "helperPid: " << (int) helperPid << endl;
|
|
|
|
TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
|
|
|
|
bool terminate_connection = args->isSet("k");
|
|
if (args->isSet("r"))
|
|
return RuleSet::checkRuleFile(args->getOption("r"));
|
|
|
|
TESTING = args->isSet("T");
|
|
|
|
// make sure that nobody can read the password from the
|
|
// config file
|
|
TQString configFile = KGlobal::dirs()->saveLocation("config")
|
|
+ TQString(kapp->name()) + "rc";
|
|
if(access(TQFile::encodeName(configFile), F_OK) == 0)
|
|
chmod(TQFile::encodeName(configFile), S_IRUSR | S_IWUSR);
|
|
|
|
// do we really need to generate an empty directory structure here ?
|
|
KGlobal::dirs()->saveLocation("appdata", "Rules");
|
|
|
|
int pid = create_pidfile();
|
|
TQString err_msg = i18n("kppp can't create or read from\n%1.").arg(pidfile);
|
|
|
|
if(pid < 0) {
|
|
KMessageBox::error(0L, err_msg);
|
|
return 1;
|
|
}
|
|
|
|
if (terminate_connection) {
|
|
setgid(getgid());
|
|
setuid(getuid());
|
|
if (pid > 0)
|
|
kill(pid, SIGINT);
|
|
else
|
|
remove_pidfile();
|
|
return 0;
|
|
}
|
|
|
|
// Mario: testing
|
|
if(TESTING) {
|
|
gpppdata.open();
|
|
gpppdata.setAccountByIndex(0);
|
|
|
|
TQString s = argv[2];
|
|
urlEncode(s);
|
|
kdDebug(5002) << s << endl;
|
|
|
|
remove_pidfile();
|
|
return 0;
|
|
}
|
|
|
|
if (pid > 0) {
|
|
TQString msg = i18n("kppp has detected a %1 file.\n"
|
|
"Another instance of kppp seems to be "
|
|
"running under process-ID %2.\n"
|
|
"Please click Exit, make sure that you are "
|
|
"not running another kppp, delete the pid "
|
|
"file, and restart kppp.\n"
|
|
"Alternatively, if you have determined that "
|
|
"there is no other kppp running, please "
|
|
"click Continue to begin.")
|
|
.arg(pidfile).arg(pid);
|
|
int button = KMessageBox::warningYesNo(0, msg, i18n("Error"),
|
|
i18n("Exit"), KStdGuiItem::cont());
|
|
if (button == KMessageBox::Yes) /* exit */
|
|
return 1;
|
|
|
|
remove_pidfile();
|
|
pid = create_pidfile();
|
|
if(pid) {
|
|
KMessageBox::error(0L, err_msg);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
KPPPWidget kppp;
|
|
p_kppp = &kppp;
|
|
|
|
(void)new DockWidget(p_kppp->con_win, "dockw", p_kppp->stats);
|
|
|
|
a.setMainWidget(&kppp);
|
|
a.setTopWidget(&kppp);
|
|
|
|
// we really don't want to die accidentally, since that would leave the
|
|
// modem connected. If you really really want to kill me you must send
|
|
// me a SIGKILL.
|
|
signal(SIGINT, sighandler);
|
|
signal(SIGCHLD, sighandler);
|
|
signal(SIGUSR1, sighandler);
|
|
signal(SIGTERM, SIG_IGN);
|
|
|
|
// XSetErrorHandler( kppp_x_errhandler );
|
|
// XSetIOErrorHandler( kppp_xio_errhandler );
|
|
|
|
int ret = a.exec();
|
|
|
|
remove_pidfile();
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
pid_t execute_command (const TQString & cmd) {
|
|
TQCString command = TQFile::encodeName(cmd);
|
|
if (command.isEmpty() || command.length() > COMMAND_SIZE)
|
|
return (pid_t) -1;
|
|
|
|
pid_t id;
|
|
|
|
kdDebug(5002) << "Executing command: " << command << endl;
|
|
|
|
TQApplication::flushX();
|
|
if((id = fork()) == 0) {
|
|
// don't bother dieppp()
|
|
signal(SIGCHLD, SIG_IGN);
|
|
|
|
// close file descriptors
|
|
const int open_max = sysconf( _SC_OPEN_MAX );
|
|
for (int fd = 3; fd < open_max; ++fd)
|
|
close(fd);
|
|
|
|
// drop privileges if running setuid root
|
|
setgid(getgid());
|
|
setuid(getuid());
|
|
|
|
system(command);
|
|
_exit(0);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
// Create a file containing the current pid. Returns 0 on success,
|
|
// -1 on failure or the pid of an already running kppp process.
|
|
pid_t create_pidfile() {
|
|
int fd = -1;
|
|
char pidstr[40]; // safe
|
|
|
|
pidfile = KGlobal::dirs()->saveLocation("appdata") + "kppp.pid";
|
|
|
|
if(access(TQFile::encodeName(pidfile), F_OK) == 0) {
|
|
|
|
if((access(TQFile::encodeName(pidfile), R_OK) < 0) ||
|
|
(fd = open(TQFile::encodeName(pidfile), O_RDONLY)) < 0)
|
|
return -1;
|
|
|
|
int sz = read(fd, &pidstr, 32);
|
|
close (fd);
|
|
if (sz < 0)
|
|
return -1;
|
|
pidstr[sz] = '\0';
|
|
|
|
kdDebug(5002) << "found kppp.pid containing: " << pidstr << endl;
|
|
|
|
// non-empty file ?
|
|
if (sz > 0) {
|
|
int oldpid;
|
|
int match = sscanf(pidstr, "%d", &oldpid);
|
|
|
|
// found a pid in pidfile ?
|
|
if (match < 1 || oldpid <= 0)
|
|
return -1;
|
|
|
|
// check if process exists
|
|
if (kill((pid_t)oldpid, 0) == 0 || errno != ESRCH)
|
|
return oldpid;
|
|
}
|
|
|
|
kdDebug(5002) << "pidfile is stale\n" << endl;
|
|
remove_pidfile();
|
|
}
|
|
|
|
if((fd = open(TQFile::encodeName(pidfile), O_WRONLY | O_CREAT | O_EXCL,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
|
|
return -1;
|
|
|
|
fchown(fd, getuid(), getgid());
|
|
|
|
sprintf(pidstr, "%d\n", getpid());
|
|
write(fd, pidstr, strlen(pidstr));
|
|
close(fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool remove_pidfile() {
|
|
struct stat st;
|
|
|
|
// only remove regular files with user write permissions
|
|
if(stat(TQFile::encodeName(pidfile), &st) == 0 )
|
|
if(S_ISREG(st.st_mode) && (access(TQFile::encodeName(pidfile), W_OK) == 0)) {
|
|
unlink(TQFile::encodeName(pidfile));
|
|
return true;
|
|
}
|
|
|
|
fprintf(stderr, "error removing pidfile.\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
void myShutDown() {
|
|
pid_t pid;
|
|
// don't bother about SIGCHLDs anymore
|
|
signal(SIGCHLD, SIG_IGN);
|
|
// fprintf(stderr, "myShutDown(%i)\n", status);
|
|
pid = helperPid;
|
|
if(pid > 0) {
|
|
helperPid = -1;
|
|
// fprintf(stderr, "killing child process %i", pid);
|
|
kill(pid, SIGKILL);
|
|
}
|
|
}
|
|
|
|
void sighandler(int sig) {
|
|
TQEvent *e = 0L;
|
|
if(sig == SIGCHLD) {
|
|
pid_t id = wait(0L);
|
|
if(id >= 0 && id == helperPid) // helper process died
|
|
e = new SignalEvent(sig);
|
|
} else if(sig == SIGINT || sig == SIGUSR1)
|
|
e = new SignalEvent(sig);
|
|
|
|
// let eventFilter() deal with this when we're back in the loop
|
|
if (e)
|
|
TQApplication::postEvent(p_kppp, e);
|
|
|
|
signal(sig, sighandler); // reinstall signal handler
|
|
}
|
|
|