* move Qt 3 support to subdirs which need it
[kdenetwork.git] / kppp / main.cpp
bloba1970152120626e2c16a3e68db58079fd7ce70d6
1 /*
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>
8 * based on EzPPP:
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>
28 #include "main.h"
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <sys/socket.h>
34 #include <locale.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
39 #ifdef _XPG4_2
40 #define __xnet_connect connect
41 #endif
43 #ifdef HAVE_SYS_PARAM_H
44 #include <sys/param.h>
45 #endif
47 #include <kaboutdata.h>
48 #include <kapplication.h>
49 #include <kcmdlineargs.h>
50 #include <kdebug.h>
51 #include <kglobal.h>
52 #include <klocale.h>
53 #include <kmessagebox.h>
54 #include <kstandarddirs.h>
56 #include "kpppwidget.h"
57 #include "opener.h"
58 #include "pppdata.h"
59 #include "providerdb.h"
60 #include "version.h"
61 #include "requester.h"
63 //#include <X11/Xlib.h>
64 //Added by qt3to4:
65 #include <QEvent>
67 static const char description[] =
68 I18N_NOOP("A dialer and front-end to pppd");
71 KPPPWidget* p_kppp;
73 // for testing purposes
74 bool TESTING=0;
76 // initial effective user id before possible suid status is dropped
77 uid_t euid;
78 // helper process' pid
79 pid_t helperPid = -1;
81 QString local_ip_address;
82 QString remote_ip_address;
83 QString pidfile;
85 #if 0
86 extern "C" {
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();
96 removedns();
97 unlockdevice();*/
99 XGetErrorText( dpy, err->error_code, errstr, 256 );
100 kFatal() << "X Error: " << errstr;
101 kFatal() << "Major opcode: " << err->request_code;
102 exit(256);
103 return 0;
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();
114 removedns();
115 Modem::modem->unlockdevice();
116 return 0;
117 } else{
118 kFatal() << "Fatal IO error: client killed";
119 exit(256);
120 return 0;
123 } /* extern "C" */
124 #endif
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.
142 int sockets[2];
143 if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) != 0) {
144 fprintf(stderr, "error creating socketpair !\n");
145 return 1;
148 switch(helperPid = fork()) {
149 case 0:
150 // child process
151 // make process leader of new group
152 setsid();
153 umask(0);
154 close(sockets[0]);
155 signal(SIGHUP, SIG_IGN);
156 (void) new Opener(sockets[1]);
157 // we should never get here
158 _exit(1);
160 case -1:
161 perror("fork() failed");
162 exit(1);
165 // parent process
166 close(sockets[1]);
168 // drop setuid status
169 euid = geteuid();
170 if (setgid(getgid()) < 0 && errno != EPERM) {
171 perror("setgid() failed");
172 exit(1);
174 setuid(getuid());
175 if (geteuid() != getuid()) {
176 perror("setuid() failed");
177 exit(1);
181 // end of setuid-dropping block.
184 // install exit handler that will kill the helper process
185 atexit(myShutDown);
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 );
214 KApplication a;
216 // set portable locale for decimal point
217 setlocale(LC_NUMERIC ,"C");
219 // open configuration file
220 gpppdata.open();
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
233 // config file
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);
245 if(pid < 0) {
246 KMessageBox::error(0L, err_msg);
247 return 1;
250 if (terminate_connection) {
251 setgid(getgid());
252 setuid(getuid());
253 if (pid > 0)
254 kill(pid, SIGINT);
255 else
256 remove_pidfile();
257 return 0;
260 // Mario: testing
261 if(TESTING) {
262 gpppdata.open();
263 gpppdata.setAccountByIndex(0);
265 QString s = argv[2];
266 urlEncode(s);
267 kDebug(5002) << s;
269 remove_pidfile();
270 return 0;
273 if (pid > 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.",
283 pidfile, pid);
284 int button = KMessageBox::warningYesNo(0, msg, i18n("Error"),
285 KGuiItem(i18n("Exit")), KStandardGuiItem::cont());
286 if (button == KMessageBox::Yes) /* exit */
287 return 1;
289 remove_pidfile();
290 pid = create_pidfile();
291 if(pid) {
292 KMessageBox::error(0L, err_msg);
293 return 1;
297 KPPPWidget kppp;
298 p_kppp = &kppp;
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
304 // me a SIGKILL.
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 );
313 int ret = a.exec();
315 remove_pidfile();
317 return ret;
321 pid_t execute_command (const QString & cmd) {
322 QByteArray command = QFile::encodeName(cmd);
323 if (command.isEmpty() || command.length() > COMMAND_SIZE)
324 return (pid_t) -1;
326 pid_t id;
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)
338 close(fd);
340 // drop privileges if running setuid root
341 setgid(getgid());
342 setuid(getuid());
344 system(command);
345 _exit(0);
348 return id;
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() {
355 int fd = -1;
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)
364 return -1;
366 int sz = read(fd, &pidstr, 32);
367 close (fd);
368 if (sz < 0)
369 return -1;
370 pidstr[sz] = '\0';
372 kDebug(5002) << "found kppp.pid containing: " << pidstr;
374 // non-empty file ?
375 if (sz > 0) {
376 int oldpid;
377 int match = sscanf(pidstr, "%d", &oldpid);
379 // found a pid in pidfile ?
380 if (match < 1 || oldpid <= 0)
381 return -1;
383 // check if process exists
384 if (kill((pid_t)oldpid, 0) == 0 || errno != ESRCH)
385 return oldpid;
388 kDebug(5002) << "pidfile is stale\n";
389 remove_pidfile();
392 if((fd = open(QFile::encodeName(pidfile), O_WRONLY | O_CREAT | O_EXCL,
393 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
394 return -1;
396 fchown(fd, getuid(), getgid());
398 sprintf(pidstr, "%d\n", getpid());
399 write(fd, pidstr, strlen(pidstr));
400 close(fd);
402 return 0;
406 bool remove_pidfile() {
407 struct stat st;
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));
413 return true;
416 fprintf(stderr, "error removing pidfile.\n");
417 return false;
421 void myShutDown() {
422 pid_t pid;
423 // don't bother about SIGCHLDs anymore
424 signal(SIGCHLD, SIG_IGN);
425 // fprintf(stderr, "myShutDown(%i)\n", status);
426 pid = helperPid;
427 if(pid > 0) {
428 helperPid = -1;
429 // fprintf(stderr, "killing child process %i", pid);
430 kill(pid, SIGKILL);
434 void sighandler(int sig) {
435 QEvent *e = 0L;
436 if(sig == SIGCHLD) {
437 pid_t id = wait(0L);
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
444 if (e)
445 QApplication::postEvent(p_kppp, e);
447 signal(sig, sighandler); // reinstall signal handler