summaryrefslogtreecommitdiffstats
path: root/kinit/start_tdeinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'kinit/start_tdeinit.c')
-rw-r--r--kinit/start_tdeinit.c191
1 files changed, 191 insertions, 0 deletions
diff --git a/kinit/start_tdeinit.c b/kinit/start_tdeinit.c
new file mode 100644
index 000000000..9d2626057
--- /dev/null
+++ b/kinit/start_tdeinit.c
@@ -0,0 +1,191 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2006 Lubos Lunak <l.lunak@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library 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 library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef KDEINIT_OOM_PROTECT
+
+/*
+ Prevent getting killed by bad heuristic in Linux OOM-killer.
+ This wrapper decreases the chance OOM killer kills it (or its children,
+ namely tdeinit), opens a pipe and forks. Child drops privileges
+ and launches tdeinit. Since processes started by tdeinit should
+ not have this protection, tdeinit will after forking send the new
+ PID using the pipe and wait for a signal. This parent will reset the protection
+ and SIGUSR1 the process to continue.
+ returns 1 if pid is valid
+*/
+
+static int set_protection( pid_t pid, int enable )
+{
+ char buf[ 1024 ];
+ int procfile;
+ struct stat st;
+
+ /* Newer kernels (noticed in 2.6.36) */
+ sprintf( buf, "/proc/%d/oom_score_adj", pid );
+ if ( lstat (buf, &st) == 0) {
+ if( !enable ) {
+ /* Be paranoid and check that the pid we got from the pipe
+ belongs to this user. */
+ if( st.st_uid != getuid())
+ return 0;
+ }
+ procfile = open(buf, O_WRONLY);
+ if( enable )
+ write( procfile, "-300", sizeof( "-300" ));
+ else
+ write( procfile, "0", sizeof( "0" ));
+ close( procfile );
+ return 1;
+ }
+
+ sprintf( buf, "/proc/%d/stat", pid );
+ if( !enable ) {
+ /* Be paranoid and check that the pid we got from the pipe
+ belongs to this user. */
+ if( lstat( buf, &st ) < 0 || st.st_uid != getuid())
+ return 0;
+ }
+ sprintf( buf, "/proc/%d/oom_adj", pid );
+ procfile = open( buf, O_WRONLY );
+ if( procfile >= 0 ) {
+ if( enable )
+ write( procfile, "-5", sizeof( "-5" ));
+ else
+ write( procfile, "0", sizeof( "0" ));
+ close( procfile );
+ }
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int pipes[ 2 ];
+ int new_argc;
+ const char** new_argv;
+ char helper_num[ 1024 ];
+ unsigned i;
+ char** orig_environ = NULL;
+ char header[ 7 ];
+ if( pipe( pipes ) < 0 ) {
+ perror( "pipe()" );
+ return 1;
+ }
+ if( argc < 0 || argc > 1000 )
+ abort(); /* paranoid */
+ set_protection( getpid(), 1 );
+ switch( fork()) {
+ case -1:
+ perror( "fork()" );
+ return 1;
+ default: /* parent, drop privileges and exec */
+ if (setgid(getgid())) {
+ perror("setgid()");
+ return 1;
+ }
+ if (setuid(getuid()) || geteuid() != getuid()) {
+ perror("setuid()");
+ return 1;
+ }
+ close( pipes[ 0 ] );
+ /* read original environment passed by start_tdeinit_wrapper */
+ if( read( 0, header, 7 ) == 7 && strncmp( header, "environ", 7 ) == 0 ) {
+ unsigned count;
+ if( read( 0, &count, sizeof( unsigned )) == sizeof( unsigned )
+ && count && count < (1<<16)) {
+ char** env = malloc(( count + 1 ) * sizeof( char* ));
+ int ok = 1;
+ for( i = 0;
+ i < count && ok;
+ ++i ) {
+ unsigned len;
+ if( read( 0, &len, sizeof( unsigned )) == sizeof( unsigned )
+ && len && len < (1<<12)) {
+ env[ i ] = malloc( len + 1 );
+ if( (unsigned) read( 0, env[ i ], len ) == len ) {
+ env[ i ][ len ] = '\0';
+ } else {
+ ok = 0;
+ }
+ }
+ }
+ if( ok ) {
+ env[ i ] = NULL;
+ orig_environ = env;
+ }
+ }
+ }
+ if(argc == 0)
+ return 1;
+ new_argc = argc + 2;
+ new_argv = malloc( sizeof( char* ) * ( new_argc + 1 ));
+ if( new_argv == NULL )
+ return 1;
+ new_argv[ 0 ] = EXECUTE;
+ new_argv[ 1 ] = "--oom-pipe";
+ sprintf( helper_num, "%d", pipes[ 1 ] );
+ new_argv[ 2 ] = helper_num;
+ for( i = 1;
+ i <= (unsigned) argc;
+ ++i )
+ new_argv[ i + 2 ] = argv[ i ];
+ if( orig_environ )
+ execve(EXECUTE, (char**)new_argv, orig_environ);
+ else
+ execv(EXECUTE, (char**)new_argv);
+ perror(EXECUTE);
+ return 1;
+ case 0: /* child, keep privileges and do the privileged work */
+ close( pipes[ 1 ] );
+ for(;;) {
+ pid_t pid = 0;
+ int ret = read( pipes[ 0 ], &pid, sizeof( pid_t ));
+ if( ret < 0 && errno == EINTR )
+ continue;
+ if( ret <= 0 ) /* pipe closed or error, exit */
+ _exit(0);
+ if( pid != 0 ) {
+ if (set_protection( pid, 0 ))
+ kill( pid, SIGUSR1 );
+ }
+ }
+ }
+}
+
+#else /* not Linux, the simple non-setuid case */
+
+int main(int argc, char **argv)
+{
+ if(argc == 0)
+ return 1;
+ argv[0] = (char*)EXECUTE;
+ execv(EXECUTE,argv);
+ perror(EXECUTE);
+ return 1;
+}
+#endif