summaryrefslogtreecommitdiffstats
path: root/kppp/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kppp/main.cpp')
-rw-r--r--kppp/main.cpp453
1 files changed, 453 insertions, 0 deletions
diff --git a/kppp/main.cpp b/kppp/main.cpp
new file mode 100644
index 00000000..96476851
--- /dev/null
+++ b/kppp/main.cpp
@@ -0,0 +1,453 @@
+/*
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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;
+
+QString local_ip_address;
+QString remote_ip_address;
+QString 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, "");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options );
+
+
+
+ KApplication a;
+
+ // set portable locale for decimal point
+ setlocale(LC_NUMERIC ,"C");
+
+ // open configuration file
+ gpppdata.open();
+
+ kdDebug(5002) << "helperPid: " << (int) helperPid << endl;
+
+ KCmdLineArgs *args = KCmdLineArgs::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
+ QString configFile = KGlobal::dirs()->saveLocation("config")
+ + QString(kapp->name()) + "rc";
+ if(access(QFile::encodeName(configFile), F_OK) == 0)
+ chmod(QFile::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();
+ QString 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);
+
+ QString s = argv[2];
+ urlEncode(s);
+ kdDebug(5002) << s << endl;
+
+ remove_pidfile();
+ return 0;
+ }
+
+ if (pid > 0) {
+ QString 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 QString & cmd) {
+ QCString command = QFile::encodeName(cmd);
+ if (command.isEmpty() || command.length() > COMMAND_SIZE)
+ return (pid_t) -1;
+
+ pid_t id;
+
+ kdDebug(5002) << "Executing command: " << command << endl;
+
+ QApplication::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(QFile::encodeName(pidfile), F_OK) == 0) {
+
+ if((access(QFile::encodeName(pidfile), R_OK) < 0) ||
+ (fd = open(QFile::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(QFile::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(QFile::encodeName(pidfile), &st) == 0 )
+ if(S_ISREG(st.st_mode) && (access(QFile::encodeName(pidfile), W_OK) == 0)) {
+ unlink(QFile::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) {
+ QEvent *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)
+ QApplication::postEvent(p_kppp, e);
+
+ signal(sig, sighandler); // reinstall signal handler
+}
+