2012-07-13 16:41:57 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2010 ProFUSION embedded systems
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <sys/wait.h>
# include <signal.h>
# include <errno.h>
2013-04-02 00:48:40 +04:00
# include <unistd.h>
2012-07-13 16:41:57 +04:00
# include "util.h"
# include "def.h"
# include "killall.h"
2013-04-02 00:48:40 +04:00
# include "set.h"
2012-07-13 16:41:57 +04:00
2013-04-02 00:48:40 +04:00
# define TIMEOUT_USEC (10 * USEC_PER_SEC)
2012-07-13 16:41:57 +04:00
static bool ignore_proc ( pid_t pid ) {
2013-04-18 01:19:38 +04:00
_cleanup_fclose_ FILE * f = NULL ;
2013-06-27 03:48:32 +04:00
char c , * p ;
2012-07-13 16:41:57 +04:00
size_t count ;
uid_t uid ;
int r ;
/* We are PID 1, let's not commit suicide */
if ( pid = = 1 )
return true ;
r = get_process_uid ( pid , & uid ) ;
if ( r < 0 )
return true ; /* not really, but better safe than sorry */
/* Non-root processes otherwise are always subject to be killed */
if ( uid ! = 0 )
return false ;
2013-06-27 03:48:32 +04:00
p = procfs_file_alloca ( pid , " cmdline " ) ;
f = fopen ( p , " re " ) ;
2012-07-13 16:41:57 +04:00
if ( ! f )
return true ; /* not really, but has the desired effect */
count = fread ( & c , 1 , 1 , f ) ;
/* Kernel threads have an empty cmdline */
if ( count < = 0 )
return true ;
/* Processes with argv[0][0] = '@' we ignore from the killing
* spree .
*
* http : //www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
if ( count = = 1 & & c = = ' @ ' )
return true ;
return false ;
}
2013-04-02 00:48:40 +04:00
static void wait_for_children ( Set * pids , sigset_t * mask ) {
2012-07-13 16:41:57 +04:00
usec_t until ;
assert ( mask ) ;
2013-04-02 00:48:40 +04:00
if ( set_isempty ( pids ) )
return ;
2012-07-13 16:41:57 +04:00
until = now ( CLOCK_MONOTONIC ) + TIMEOUT_USEC ;
for ( ; ; ) {
struct timespec ts ;
int k ;
usec_t n ;
2013-04-02 00:48:40 +04:00
void * p ;
Iterator i ;
2012-07-13 16:41:57 +04:00
2013-04-02 00:48:40 +04:00
/* First, let the kernel inform us about killed
* children . Most processes will probably be our
* children , but some are not ( might be our
* grandchildren instead . . . ) . */
2012-07-13 16:41:57 +04:00
for ( ; ; ) {
2013-04-02 00:48:40 +04:00
pid_t pid ;
2012-07-13 16:41:57 +04:00
2013-04-02 00:48:40 +04:00
pid = waitpid ( - 1 , NULL , WNOHANG ) ;
2012-07-13 16:41:57 +04:00
if ( pid = = 0 )
break ;
2013-04-02 00:48:40 +04:00
if ( pid < 0 ) {
if ( errno = = ECHILD )
break ;
2012-07-13 16:41:57 +04:00
2013-04-02 00:48:40 +04:00
log_error ( " waitpid() failed: %m " ) ;
2012-07-13 16:41:57 +04:00
return ;
2013-04-02 00:48:40 +04:00
}
set_remove ( pids , ULONG_TO_PTR ( pid ) ) ;
}
2012-07-13 16:41:57 +04:00
2013-04-02 00:48:40 +04:00
/* Now explicitly check who might be remaining, who
* might not be our child . */
SET_FOREACH ( p , pids , i ) {
/* We misuse getpgid as a check whether a
* process still exists . */
if ( getpgid ( ( pid_t ) PTR_TO_ULONG ( p ) ) > = 0 )
continue ;
if ( errno ! = ESRCH )
continue ;
set_remove ( pids , p ) ;
2012-07-13 16:41:57 +04:00
}
2013-04-02 00:48:40 +04:00
if ( set_isempty ( pids ) )
return ;
2012-07-13 16:41:57 +04:00
n = now ( CLOCK_MONOTONIC ) ;
if ( n > = until )
return ;
timespec_store ( & ts , until - n ) ;
2013-04-02 00:48:40 +04:00
k = sigtimedwait ( mask , NULL , & ts ) ;
if ( k ! = SIGCHLD ) {
2012-07-13 16:41:57 +04:00
if ( k < 0 & & errno ! = EAGAIN ) {
log_error ( " sigtimedwait() failed: %m " ) ;
return ;
}
if ( k > = 0 )
log_warning ( " sigtimedwait() returned unexpected signal. " ) ;
}
}
}
2013-04-02 00:48:40 +04:00
static int killall ( int sig , Set * pids ) {
_cleanup_closedir_ DIR * dir = NULL ;
2012-07-13 16:41:57 +04:00
struct dirent * d ;
dir = opendir ( " /proc " ) ;
if ( ! dir )
return - errno ;
while ( ( d = readdir ( dir ) ) ) {
pid_t pid ;
if ( d - > d_type ! = DT_DIR & &
d - > d_type ! = DT_UNKNOWN )
continue ;
if ( parse_pid ( d - > d_name , & pid ) < 0 )
continue ;
if ( ignore_proc ( pid ) )
continue ;
2013-03-29 02:00:32 +04:00
if ( sig = = SIGKILL ) {
_cleanup_free_ char * s ;
get_process_comm ( pid , & s ) ;
2013-04-02 00:48:40 +04:00
log_notice ( " Sending SIGKILL to PID %lu (%s). " , ( unsigned long ) pid , strna ( s ) ) ;
2013-03-29 02:00:32 +04:00
}
2013-04-02 00:48:40 +04:00
if ( kill ( pid , sig ) > = 0 ) {
if ( pids )
set_put ( pids , ULONG_TO_PTR ( ( unsigned long ) pid ) ) ;
} else if ( errno ! = ENOENT )
2012-07-13 16:41:57 +04:00
log_warning ( " Could not kill %d: %m " , pid ) ;
}
2013-04-02 00:48:40 +04:00
return set_size ( pids ) ;
2012-07-13 16:41:57 +04:00
}
2012-07-19 04:17:11 +04:00
void broadcast_signal ( int sig , bool wait_for_exit ) {
2012-07-13 16:41:57 +04:00
sigset_t mask , oldmask ;
2013-04-03 17:16:06 +04:00
Set * pids = NULL ;
2013-04-02 00:48:40 +04:00
if ( wait_for_exit )
pids = set_new ( trivial_hash_func , trivial_compare_func ) ;
2012-07-13 16:41:57 +04:00
assert_se ( sigemptyset ( & mask ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGCHLD ) = = 0 ) ;
assert_se ( sigprocmask ( SIG_BLOCK , & mask , & oldmask ) = = 0 ) ;
if ( kill ( - 1 , SIGSTOP ) < 0 & & errno ! = ESRCH )
log_warning ( " kill(-1, SIGSTOP) failed: %m " ) ;
2013-04-02 00:48:40 +04:00
killall ( sig , pids ) ;
2012-07-13 16:41:57 +04:00
if ( kill ( - 1 , SIGCONT ) < 0 & & errno ! = ESRCH )
log_warning ( " kill(-1, SIGCONT) failed: %m " ) ;
2012-07-19 04:17:11 +04:00
if ( wait_for_exit )
2013-04-02 00:48:40 +04:00
wait_for_children ( pids , & mask ) ;
assert_se ( sigprocmask ( SIG_SETMASK , & oldmask , NULL ) = = 0 ) ;
2012-07-13 16:41:57 +04:00
2013-04-02 00:48:40 +04:00
set_free ( pids ) ;
2012-07-13 16:41:57 +04:00
}