add more spacing
[personal-kdebase.git] / runtime / kdesu / kdesud / kdesud.cpp
blob266db66c9149eaddf1c762bee38d1e5c7ae565db
1 /* vi: ts=8 sts=4 sw=4
3 * This file is part of the KDE project, module kdesu.
4 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
7 * kdesud.cpp: KDE su daemon. Offers "keep password" functionality to kde su.
9 * The socket $KDEHOME/socket-$(HOSTNAME)/kdesud_$(display) is used for communication with
10 * client programs.
12 * The protocol: Client initiates the connection. All commands and responses
13 * are terminated by a newline.
15 * Client Server Description
16 * ------ ------ -----------
18 * PASS <pass> <timeout> OK Set password for commands in
19 * this session. Password is
20 * valid for <timeout> seconds.
22 * USER <user> OK Set the target user [required]
24 * EXEC <command> OK Execute command <command>. If
25 * NO <command> has been executed
26 * before (< timeout) no PASS
27 * command is needed.
29 * DEL <command> OK Delete password for command
30 * NO <command>.
32 * PING OK Ping the server (diagnostics).
36 #include <config-runtime.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <stdarg.h>
44 #include <signal.h>
45 #include <pwd.h>
46 #include <errno.h>
48 #include <sys/time.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <sys/un.h>
53 #include <sys/resource.h>
54 #include <sys/wait.h>
55 #ifdef HAVE_SYS_SELECT_H
56 #include <sys/select.h> // Needed on some systems.
57 #endif
59 #include <QVector>
60 #include <QFile>
61 #include <QRegExp>
62 #include <QByteArray>
64 #include <kcomponentdata.h>
65 #include <kdebug.h>
66 #include <klocale.h>
67 #include <kcmdlineargs.h>
68 #include <kstandarddirs.h>
69 #include <kaboutdata.h>
70 #include <kdesu/client.h>
71 #include <kdesu/defaults.h>
73 #include "repo.h"
74 #include "handler.h"
76 #ifdef Q_WS_X11
77 #include <X11/X.h>
78 #include <X11/Xlib.h>
79 #endif
81 #ifndef SUN_LEN
82 #define SUN_LEN(ptr) ((kde_socklen_t) (((struct sockaddr_un *) 0)->sun_path) \
83 + strlen ((ptr)->sun_path))
84 #endif
86 #define ERR strerror(errno)
89 using namespace KDESu;
91 // Globals
93 Repository *repo;
94 const char *Version = "1.01";
95 QByteArray sock;
96 #ifdef Q_WS_X11
97 Display *x11Display;
98 #endif
99 int pipeOfDeath[2];
101 // FIXME: This is just here to make it compile
102 // It would be better to fix it more globally (Caleb Tennis)
103 typedef unsigned ksocklen_t;
105 void kdesud_cleanup()
107 unlink(sock);
111 // Borrowed from kdebase/kaudio/kaudioserver.cpp
113 #ifdef Q_WS_X11
114 extern "C" int xio_errhandler(Display *);
116 int xio_errhandler(Display *)
118 kError(1205) << "Fatal IO error, exiting...\n";
119 kdesud_cleanup();
120 exit(1);
121 return 1; //silence compilers
124 int initXconnection()
126 x11Display = XOpenDisplay(NULL);
127 if (x11Display != 0L)
129 XSetIOErrorHandler(xio_errhandler);
130 XCreateSimpleWindow(x11Display, DefaultRootWindow(x11Display),
131 0, 0, 1, 1, 0,
132 BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)),
133 BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)));
134 return XConnectionNumber(x11Display);
135 } else
137 kWarning(1205) << "Can't connect to the X Server.\n";
138 kWarning(1205) << "Might not terminate at end of session.\n";
139 return -1;
142 #endif
144 extern "C" {
145 void signal_exit(int);
146 void sigchld_handler(int);
149 void signal_exit(int sig)
151 kDebug(1205) << "Exiting on signal " << sig << "\n";
152 kdesud_cleanup();
153 exit(1);
156 void sigchld_handler(int)
158 char c = ' ';
159 write(pipeOfDeath[1], &c, 1);
163 * Creates an AF_UNIX socket in socket resource, mode 0600.
166 int create_socket()
168 int sockfd;
169 ksocklen_t addrlen;
170 struct stat s;
172 QString display = QString::fromAscii(getenv("DISPLAY"));
173 if (display.isEmpty())
175 kWarning(1205) << "$DISPLAY is not set\n";
176 return -1;
179 // strip the screen number from the display
180 display.replace(QRegExp("\\.[0-9]+$"), "");
182 sock = QFile::encodeName(KStandardDirs::locateLocal("socket", QString("kdesud_%1").arg(display)));
183 int stat_err=lstat(sock, &s);
184 if(!stat_err && S_ISLNK(s.st_mode)) {
185 kWarning(1205) << "Someone is running a symlink attack on you\n";
186 if(unlink(sock)) {
187 kWarning(1205) << "Could not delete symlink\n";
188 return -1;
192 if (!access(sock, R_OK|W_OK))
194 KDEsuClient client;
195 if (client.ping() == -1)
197 kWarning(1205) << "stale socket exists\n";
198 if (unlink(sock))
200 kWarning(1205) << "Could not delete stale socket\n";
201 return -1;
203 } else
205 kWarning(1205) << "kdesud is already running\n";
206 return -1;
211 sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
212 if (sockfd < 0)
214 kError(1205) << "socket(): " << ERR << "\n";
215 return -1;
218 struct sockaddr_un addr;
219 addr.sun_family = AF_UNIX;
220 strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1);
221 addr.sun_path[sizeof(addr.sun_path)-1] = '\000';
222 addrlen = SUN_LEN(&addr);
223 if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0)
225 kError(1205) << "bind(): " << ERR << "\n";
226 return -1;
229 struct linger lin;
230 lin.l_onoff = lin.l_linger = 0;
231 if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lin,
232 sizeof(linger)) < 0)
234 kError(1205) << "setsockopt(SO_LINGER): " << ERR << "\n";
235 return -1;
238 int opt = 1;
239 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt,
240 sizeof(opt)) < 0)
242 kError(1205) << "setsockopt(SO_REUSEADDR): " << ERR << "\n";
243 return -1;
245 opt = 1;
246 if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt,
247 sizeof(opt)) < 0)
249 kError(1205) << "setsockopt(SO_KEEPALIVE): " << ERR << "\n";
250 return -1;
252 chmod(sock, 0600);
253 return sockfd;
258 * Main program
261 int main(int argc, char *argv[])
263 KAboutData aboutData("kdesud", 0, ki18n("KDE su daemon"),
264 Version, ki18n("Daemon used by kdesu"),
265 KAboutData::License_Artistic,
266 ki18n("Copyright (c) 1999,2000 Geert Jansen"));
267 aboutData.addAuthor(ki18n("Geert Jansen"), ki18n("Author"),
268 "jansen@kde.org", "http://www.stack.nl/~geertj/");
269 KCmdLineArgs::init(argc, argv, &aboutData);
270 KComponentData componentData(&aboutData);
272 // Set core dump size to 0
273 struct rlimit rlim;
274 rlim.rlim_cur = rlim.rlim_max = 0;
275 if (setrlimit(RLIMIT_CORE, &rlim) < 0)
277 kError(1205) << "setrlimit(): " << ERR << "\n";
278 exit(1);
281 // Create the Unix socket.
282 int sockfd = create_socket();
283 if (sockfd < 0)
284 exit(1);
285 if (listen(sockfd, 1) < 0)
287 kError(1205) << "listen(): " << ERR << "\n";
288 kdesud_cleanup();
289 exit(1);
291 int maxfd = sockfd;
293 // Ok, we're accepting connections. Fork to the background.
294 pid_t pid = fork();
295 if (pid == -1)
297 kError(1205) << "fork():" << ERR << "\n";
298 kdesud_cleanup();
299 exit(1);
301 if (pid)
302 exit(0);
304 #ifdef Q_WS_X11
305 // Make sure we exit when the display gets closed.
306 int x11Fd = initXconnection();
307 maxfd = qMax(maxfd, x11Fd);
308 #endif
310 repo = new Repository;
311 QVector<ConnectionHandler *> handler;
313 pipe(pipeOfDeath);
314 maxfd = qMax(maxfd, pipeOfDeath[0]);
316 // Signal handlers
317 struct sigaction sa;
318 sa.sa_handler = signal_exit;
319 sigemptyset(&sa.sa_mask);
320 sa.sa_flags = 0;
321 sigaction(SIGHUP, &sa, 0L);
322 sigaction(SIGINT, &sa, 0L);
323 sigaction(SIGTERM, &sa, 0L);
324 sigaction(SIGQUIT, &sa, 0L);
326 sa.sa_handler = sigchld_handler;
327 sa.sa_flags = SA_NOCLDSTOP;
328 sigaction(SIGCHLD, &sa, 0L);
329 sa.sa_handler = SIG_IGN;
330 sigaction(SIGPIPE, &sa, 0L);
332 // Main execution loop
334 ksocklen_t addrlen;
335 struct sockaddr_un clientname;
337 fd_set tmp_fds, active_fds;
338 FD_ZERO(&active_fds);
339 FD_SET(sockfd, &active_fds);
340 FD_SET(pipeOfDeath[0], &active_fds);
341 #ifdef Q_WS_X11
342 if (x11Fd != -1)
343 FD_SET(x11Fd, &active_fds);
344 #endif
346 while (1)
348 tmp_fds = active_fds;
349 #ifdef Q_WS_X11
350 if(x11Display)
351 XFlush(x11Display);
352 #endif
353 if (select(maxfd+1, &tmp_fds, 0L, 0L, 0L) < 0)
355 if (errno == EINTR) continue;
357 kError(1205) << "select(): " << ERR << "\n";
358 exit(1);
360 repo->expire();
361 for (int i=0; i<=maxfd; i++)
363 if (!FD_ISSET(i, &tmp_fds))
364 continue;
366 if (i == pipeOfDeath[0])
368 char buf[101];
369 read(pipeOfDeath[0], buf, 100);
370 pid_t result;
373 int status;
374 result = waitpid((pid_t)-1, &status, WNOHANG);
375 if (result > 0)
377 for(int j=handler.size(); j--;)
379 if (handler[j] && (handler[j]->m_pid == result))
381 handler[j]->m_exitCode = WEXITSTATUS(status);
382 handler[j]->m_hasExitCode = true;
383 handler[j]->sendExitCode();
384 handler[j]->m_pid = 0;
385 break;
390 while(result > 0);
393 #ifdef Q_WS_X11
394 if (i == x11Fd)
396 // Discard X events
397 XEvent event_return;
398 if (x11Display)
399 while(XPending(x11Display))
400 XNextEvent(x11Display, &event_return);
401 continue;
403 #endif
405 if (i == sockfd)
407 // Accept new connection
408 int fd;
409 addrlen = 64;
410 fd = accept(sockfd, (struct sockaddr *) &clientname, &addrlen);
411 if (fd < 0)
413 kError(1205) << "accept():" << ERR << "\n";
414 continue;
416 while (fd+1 > (int) handler.size())
417 handler.append(0);
418 delete handler[fd];
419 handler[fd] = new ConnectionHandler(fd);
420 maxfd = qMax(maxfd, fd);
421 FD_SET(fd, &active_fds);
422 continue;
425 // handle already established connection
426 if (handler[i] && handler[i]->handle() < 0)
428 delete handler[i];
429 handler[i] = 0;
430 FD_CLR(i, &active_fds);
434 kWarning(1205) << "???\n";