1/*
2 * This file is part of the KDE libraries
3 * Copyright (c) 2006 Lubos Lunak <l.lunak@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License version 2 as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include <config.h>
21#include <config-prefix.h>
22#include <config-kdeinit.h>
23
24#include <errno.h>
25#include <fcntl.h>
26#include <signal.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/stat.h>
31#include <unistd.h>
32
33#define EXECUTE BIN_INSTALL_DIR "/kdeinit4"
34
35#ifdef KDEINIT_OOM_PROTECT
36
37/*
38 Prevent getting killed by bad heuristic in Linux OOM-killer.
39 This wrapper decreases the chance OOM killer kills it (or its children,
40 namely kdeinit), opens a pipe and forks. Child drops privileges
41 and launches kdeinit. Since processes started by kdeinit should
42 not have this protection, kdeinit will after forking send the new
43 PID using the pipe and wait for a signal. This parent will reset the protection
44 and SIGUSR1 the process to continue.
45 returns 1 if pid is valid
46*/
47
48static int set_protection( pid_t pid, int enable )
49{
50 char buf[ 1024 ];
51 int procfile;
52 struct stat st;
53 /* Newer kernels (noticed in 2.6.36) */
54 sprintf( buf, "/proc/%d/oom_score_adj", pid );
55 if ( lstat (buf, &st) == 0) {
56 if ( !enable ) {
57 /* Be paranoid and check that the pid we got from the pipe
58 belongs to this user. */
59 if( st.st_uid != getuid())
60 return 0;
61 }
62 procfile = open(buf, O_WRONLY);
63 if( enable )
64 write( procfile, "-300", sizeof( "-300" ));
65 else
66 write( procfile, "0", sizeof( "0" ));
67 close( procfile );
68 return 1;
69 }
70
71 sprintf( buf, "/proc/%d/stat", pid );
72 if( !enable ) {
73 /* Be paranoid and check that the pid we got from the pipe
74 belongs to this user. */
75 if( lstat( buf, &st ) < 0 || st.st_uid != getuid())
76 return 0;
77 }
78 sprintf( buf, "/proc/%d/oom_adj", pid );
79 procfile = open( buf, O_WRONLY );
80 if( procfile >= 0 ) {
81 if( enable )
82 write( procfile, "-5", sizeof( "-5" ));
83 else
84 write( procfile, "0", sizeof( "0" ));
85 close( procfile );
86 }
87 return 1;
88}
89
90int main(int argc, char **argv)
91{
92 int pipes[ 2 ];
93 int new_argc;
94 const char** new_argv;
95 char helper_num[ 1024 ];
96 unsigned i;
97 char** orig_environ = NULL;
98 char header[ 7 ];
99 if( pipe( pipes ) < 0 ) {
100 perror( "pipe()" );
101 return 1;
102 }
103 if( argc < 0 || argc > 1000 )
104 abort(); /* paranoid */
105 set_protection( getpid(), 1 );
106 switch( fork()) {
107 case -1:
108 perror( "fork()" );
109 return 1;
110 default: /* parent, drop privileges and exec */
111 if (setgid(getgid())) {
112 perror("setgid()");
113 return 1;
114 }
115 if (setuid(getuid()) || geteuid() != getuid()) {
116 perror("setuid()");
117 return 1;
118 }
119 close( pipes[ 0 ] );
120 /* read original environment passed by start_kdeinit_wrapper */
121 if( read( 0, header, 7 ) == 7 && strncmp( header, "environ", 7 ) == 0 ) {
122 unsigned count;
123 if( read( 0, &count, sizeof( unsigned )) == sizeof( unsigned )
124 && count && count < (1<<16)) {
125 char** env = malloc(( count + 1 ) * sizeof( char* ));
126 int ok = 1;
127 for( i = 0;
128 i < count && ok;
129 ++i ) {
130 unsigned len;
131 if( read( 0, &len, sizeof( unsigned )) == sizeof( unsigned )
132 && len && len < (1<<12)) {
133 env[ i ] = malloc( len + 1 );
134 if( (unsigned) read( 0, env[ i ], len ) == len ) {
135 env[ i ][ len ] = '\0';
136 } else {
137 ok = 0;
138 }
139 }
140 }
141 if( ok ) {
142 env[ i ] = NULL;
143 orig_environ = env;
144 }
145 }
146 }
147 if(argc == 0)
148 return 1;
149 new_argc = argc + 2;
150 new_argv = malloc( sizeof( char* ) * ( new_argc + 1 ));
151 if( new_argv == NULL )
152 return 1;
153 new_argv[ 0 ] = EXECUTE;
154 new_argv[ 1 ] = "--oom-pipe";
155 sprintf( helper_num, "%d", pipes[ 1 ] );
156 new_argv[ 2 ] = helper_num;
157 for( i = 1;
158 i <= (unsigned) argc;
159 ++i )
160 new_argv[ i + 2 ] = argv[ i ];
161 if( orig_environ )
162 execve(EXECUTE, (char**)new_argv, orig_environ);
163 else
164 execv(EXECUTE, (char**)new_argv);
165 perror(EXECUTE);
166 return 1;
167 case 0: /* child, keep privileges and do the privileged work */
168 close( pipes[ 1 ] );
169 for(;;) {
170 pid_t pid = 0;
171 int ret = read( pipes[ 0 ], &pid, sizeof( pid_t ));
172 if( ret < 0 && errno == EINTR )
173 continue;
174 if( ret <= 0 ) /* pipe closed or error, exit */
175 _exit(0);
176 if( pid != 0 ) {
177 if (set_protection( pid, 0 ))
178 kill( pid, SIGUSR1 );
179 }
180 }
181 }
182}
183
184#else /* not Linux, the simple non-setuid case */
185
186int main(int argc, char **argv)
187{
188 if(argc == 0)
189 return 1;
190 argv[0] = (char*)EXECUTE;
191 execv(EXECUTE,argv);
192 perror(EXECUTE);
193 return 1;
194}
195#endif
196