2009-11-18 02:42:52 +03:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
2010-02-03 15:03:47 +03:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 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
General Public License for more details .
You should have received a copy of the GNU General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2010-02-01 05:33:24 +03:00
# include <dbus/dbus.h>
2009-11-18 02:42:52 +03:00
# include <stdio.h>
# include <errno.h>
# include <string.h>
2010-01-20 21:19:53 +03:00
# include <unistd.h>
2010-04-06 23:59:25 +04:00
# include <sys/types.h>
# include <sys/stat.h>
2010-04-07 01:40:24 +04:00
# include <getopt.h>
2010-04-10 23:40:40 +04:00
# include <signal.h>
2010-04-11 00:35:37 +04:00
# include <sys/wait.h>
2010-04-13 04:06:27 +04:00
# include <fcntl.h>
2009-11-18 02:42:52 +03:00
# include "manager.h"
2010-01-20 21:19:53 +03:00
# include "log.h"
2010-04-06 23:59:25 +04:00
# include "mount-setup.h"
2010-04-10 07:00:38 +04:00
# include "hostname-setup.h"
# include "load-fragment.h"
2009-11-18 02:42:52 +03:00
2010-04-07 01:40:24 +04:00
static enum {
ACTION_RUN ,
2010-04-07 02:10:17 +04:00
ACTION_HELP ,
2010-04-10 19:53:17 +04:00
ACTION_TEST ,
ACTION_DUMP_CONFIGURATION_ITEMS
2010-04-07 01:40:24 +04:00
} action = ACTION_RUN ;
static char * default_unit = NULL ;
2010-04-07 01:55:42 +04:00
static ManagerRunningAs running_as = _MANAGER_RUNNING_AS_INVALID ;
2010-04-11 00:35:37 +04:00
2010-04-10 23:40:40 +04:00
static bool dump_core = true ;
2010-04-11 01:36:43 +04:00
static bool crash_shell = false ;
static int crash_chvt = - 1 ;
2010-04-10 23:40:40 +04:00
2010-04-13 04:06:27 +04:00
static bool confirm_spawn = false ;
2010-04-10 23:40:40 +04:00
_noreturn static void freeze ( void ) {
for ( ; ; )
pause ( ) ;
}
_noreturn static void crash ( int sig ) {
if ( ! dump_core )
log_error ( " Caught <%s>, not dumping core. " , strsignal ( sig ) ) ;
else {
pid_t pid ;
if ( ( pid = fork ( ) ) < 0 )
2010-04-11 00:35:37 +04:00
log_error ( " Caught <%s>, cannot fork for core dump: %s " , strsignal ( sig ) , strerror ( errno ) ) ;
2010-04-10 23:40:40 +04:00
else if ( pid = = 0 ) {
struct sigaction sa ;
struct rlimit rl ;
/* Enable default signal handler for core dump */
zero ( sa ) ;
sa . sa_handler = SIG_DFL ;
assert_se ( sigaction ( sig , & sa , NULL ) = = 0 ) ;
/* Don't limit the core dump size */
zero ( rl ) ;
rl . rlim_cur = RLIM_INFINITY ;
rl . rlim_max = RLIM_INFINITY ;
setrlimit ( RLIMIT_CORE , & rl ) ;
/* Just to be sure... */
assert_se ( chdir ( " / " ) = = 0 ) ;
/* Raise the signal again */
raise ( sig ) ;
assert_not_reached ( " We shouldn't be here... " ) ;
_exit ( 1 ) ;
2010-04-11 00:35:37 +04:00
} else {
int status , r ;
/* Order things nicely. */
if ( ( r = waitpid ( pid , & status , 0 ) ) < 0 )
log_error ( " Caught <%s>, waitpid() failed: %s " , strsignal ( sig ) , strerror ( errno ) ) ;
else if ( ! WCOREDUMP ( status ) )
log_error ( " Caught <%s>, core dump failed. " , strsignal ( sig ) ) ;
else
log_error ( " Caught <%s>, dumped core as pid %llu. " , strsignal ( sig ) , ( unsigned long long ) pid ) ;
2010-04-10 23:40:40 +04:00
}
}
2010-04-11 01:36:43 +04:00
if ( crash_chvt )
chvt ( crash_chvt ) ;
2010-04-11 00:35:37 +04:00
if ( crash_shell ) {
log_info ( " Executing crash shell in 10s... " ) ;
sleep ( 10 ) ;
execl ( " /bin/sh " , " /bin/sh " , NULL ) ;
log_error ( " execl() failed: %s " , strerror ( errno ) ) ;
}
log_info ( " Freezing execution. " ) ;
2010-04-10 23:40:40 +04:00
freeze ( ) ;
}
static void install_crash_handler ( void ) {
struct sigaction sa ;
zero ( sa ) ;
sa . sa_handler = crash ;
sa . sa_flags = SA_NODEFER ;
assert_se ( sigaction ( SIGSEGV , & sa , NULL ) = = 0 ) ;
2010-04-13 04:00:30 +04:00
assert_se ( sigaction ( SIGILL , & sa , NULL ) = = 0 ) ;
assert_se ( sigaction ( SIGFPE , & sa , NULL ) = = 0 ) ;
assert_se ( sigaction ( SIGBUS , & sa , NULL ) = = 0 ) ;
assert_se ( sigaction ( SIGQUIT , & sa , NULL ) = = 0 ) ;
2010-04-10 23:40:40 +04:00
assert_se ( sigaction ( SIGABRT , & sa , NULL ) = = 0 ) ;
}
2010-04-07 01:40:24 +04:00
2010-04-13 04:06:27 +04:00
static int console_setup ( void ) {
int tty_fd = - 1 , null_fd = - 1 , r = 0 ;
/* If we are init, we connect stdout/stderr to /dev/console
* and stdin to / dev / null and make sure we don ' t have a
* controlling tty . */
release_terminal ( ) ;
if ( ( tty_fd = open_terminal ( " /dev/console " , O_WRONLY ) ) < 0 ) {
log_error ( " Failed to open /dev/console: %s " , strerror ( - tty_fd ) ) ;
r = - tty_fd ;
goto finish ;
}
if ( ( null_fd = open ( " /dev/null " , O_RDONLY ) ) < 0 ) {
log_error ( " Failed to open /dev/null: %m " ) ;
r = - errno ;
goto finish ;
}
assert ( tty_fd > = 3 ) ;
assert ( null_fd > = 3 ) ;
if ( reset_terminal ( tty_fd ) < 0 )
log_error ( " Failed to reset /dev/console: %m " ) ;
if ( dup2 ( tty_fd , STDOUT_FILENO ) < 0 | |
dup2 ( tty_fd , STDERR_FILENO ) < 0 | |
dup2 ( null_fd , STDIN_FILENO ) < 0 ) {
log_error ( " Failed to dup2() device: %m " ) ;
r = - errno ;
goto finish ;
}
r = 0 ;
finish :
if ( tty_fd > = 0 )
close_nointr ( tty_fd ) ;
if ( null_fd > = 0 )
close_nointr ( null_fd ) ;
return r ;
}
2010-04-07 01:40:24 +04:00
static int set_default_unit ( const char * u ) {
char * c ;
assert ( u ) ;
if ( ! ( c = strdup ( u ) ) )
return - ENOMEM ;
free ( default_unit ) ;
default_unit = c ;
return 0 ;
}
static int parse_proc_cmdline_word ( const char * word ) {
static const char * const rlmap [ ] = {
" single " , SPECIAL_RUNLEVEL1_TARGET ,
" -s " , SPECIAL_RUNLEVEL1_TARGET ,
" s " , SPECIAL_RUNLEVEL1_TARGET ,
" S " , SPECIAL_RUNLEVEL1_TARGET ,
" 1 " , SPECIAL_RUNLEVEL1_TARGET ,
" 2 " , SPECIAL_RUNLEVEL2_TARGET ,
" 3 " , SPECIAL_RUNLEVEL3_TARGET ,
" 4 " , SPECIAL_RUNLEVEL4_TARGET ,
" 5 " , SPECIAL_RUNLEVEL5_TARGET
} ;
if ( startswith ( word , " systemd.default= " ) )
2010-04-08 04:00:22 +04:00
return set_default_unit ( word + 16 ) ;
2010-04-07 01:40:24 +04:00
else if ( startswith ( word , " systemd.log_target= " ) ) {
if ( log_set_target_from_string ( word + 19 ) < 0 )
log_warning ( " Failed to parse log target %s. Ignoring. " , word + 19 ) ;
} else if ( startswith ( word , " systemd.log_level= " ) ) {
if ( log_set_max_level_from_string ( word + 18 ) < 0 )
log_warning ( " Failed to parse log level %s. Ignoring. " , word + 18 ) ;
2010-04-11 00:35:37 +04:00
} else if ( startswith ( word , " systemd.dump_core= " ) ) {
int r ;
if ( ( r = parse_boolean ( word + 18 ) ) < 0 )
2010-04-11 01:36:43 +04:00
log_warning ( " Failed to parse dump core switch %s, Ignoring. " , word + 18 ) ;
2010-04-11 00:35:37 +04:00
else
dump_core = r ;
} else if ( startswith ( word , " systemd.crash_shell= " ) ) {
int r ;
if ( ( r = parse_boolean ( word + 20 ) ) < 0 )
2010-04-11 01:36:43 +04:00
log_warning ( " Failed to parse crash shell switch %s, Ignoring. " , word + 20 ) ;
2010-04-11 00:35:37 +04:00
else
crash_shell = r ;
2010-04-11 01:36:43 +04:00
} else if ( startswith ( word , " systemd.crash_chvt= " ) ) {
int k ;
if ( safe_atoi ( word + 19 , & k ) < 0 )
log_warning ( " Failed to parse crash chvt switch %s, Ignoring. " , word + 19 ) ;
else
crash_chvt = k ;
2010-04-11 00:35:37 +04:00
} else if ( startswith ( word , " systemd. " ) ) {
log_warning ( " Unknown kernel switch %s. Ignoring. " , word ) ;
log_info ( " Supported kernel switches: " ) ;
log_info ( " systemd.default=UNIT Default unit to start " ) ;
log_info ( " systemd.log_target=console|kmsg|syslog Log target " ) ;
log_info ( " systemd.log_level=LEVEL Log level " ) ;
log_info ( " systemd.dump_core=0|1 Dump core on crash " ) ;
log_info ( " systemd.crash_shell=0|1 On crash run shell " ) ;
2010-04-11 01:36:43 +04:00
log_info ( " systemd.crash_chvt=N Change to VT #N on crash " ) ;
2010-04-11 00:35:37 +04:00
2010-04-07 01:40:24 +04:00
} else {
unsigned i ;
/* SysV compatibility */
for ( i = 0 ; i < ELEMENTSOF ( rlmap ) ; i + = 2 )
if ( streq ( word , rlmap [ i ] ) )
return set_default_unit ( rlmap [ i + 1 ] ) ;
}
return 0 ;
}
static int parse_proc_cmdline ( void ) {
char * line ;
int r ;
char * w ;
size_t l ;
char * state ;
if ( ( r = read_one_line_file ( " /proc/cmdline " , & line ) ) < 0 ) {
log_warning ( " Failed to read /proc/cmdline, ignoring: %s " , strerror ( errno ) ) ;
return 0 ;
}
FOREACH_WORD_QUOTED ( w , l , line , state ) {
char * word ;
if ( ! ( word = strndup ( w , l ) ) ) {
r = - ENOMEM ;
goto finish ;
}
r = parse_proc_cmdline_word ( word ) ;
free ( word ) ;
if ( r < 0 )
goto finish ;
}
r = 0 ;
finish :
free ( line ) ;
return r ;
}
static int parse_argv ( int argc , char * argv [ ] ) {
enum {
ARG_LOG_LEVEL = 0x100 ,
ARG_LOG_TARGET ,
2010-04-07 01:55:42 +04:00
ARG_DEFAULT ,
2010-04-07 02:10:17 +04:00
ARG_RUNNING_AS ,
2010-04-10 19:53:17 +04:00
ARG_TEST ,
2010-04-13 04:06:27 +04:00
ARG_DUMP_CONFIGURATION_ITEMS ,
ARG_CONFIRM_SPAWN
2010-04-07 01:40:24 +04:00
} ;
static const struct option options [ ] = {
{ " log-level " , required_argument , NULL , ARG_LOG_LEVEL } ,
{ " log-target " , required_argument , NULL , ARG_LOG_TARGET } ,
{ " default " , required_argument , NULL , ARG_DEFAULT } ,
2010-04-07 01:55:42 +04:00
{ " running-as " , required_argument , NULL , ARG_RUNNING_AS } ,
2010-04-07 02:10:17 +04:00
{ " test " , no_argument , NULL , ARG_TEST } ,
2010-04-07 01:40:24 +04:00
{ " help " , no_argument , NULL , ' h ' } ,
2010-04-10 19:53:17 +04:00
{ " dump-configuration-items " , no_argument , NULL , ARG_DUMP_CONFIGURATION_ITEMS } ,
2010-04-13 04:06:27 +04:00
{ " confirm-spawn " , no_argument , NULL , ARG_CONFIRM_SPAWN } ,
2010-04-07 01:40:24 +04:00
{ NULL , 0 , NULL , 0 }
} ;
int c , r ;
assert ( argc > = 1 ) ;
assert ( argv ) ;
while ( ( c = getopt_long ( argc , argv , " h " , options , NULL ) ) > = 0 )
switch ( c ) {
case ARG_LOG_LEVEL :
if ( ( r = log_set_max_level_from_string ( optarg ) ) < 0 ) {
log_error ( " Failed to parse log level %s. " , optarg ) ;
return r ;
}
break ;
case ARG_LOG_TARGET :
if ( ( r = log_set_target_from_string ( optarg ) ) < 0 ) {
log_error ( " Failed to parse log target %s. " , optarg ) ;
return r ;
}
break ;
case ARG_DEFAULT :
if ( ( r = set_default_unit ( optarg ) ) < 0 ) {
log_error ( " Failed to set default unit %s: %s " , optarg , strerror ( - r ) ) ;
return r ;
}
break ;
2010-04-07 01:55:42 +04:00
case ARG_RUNNING_AS : {
ManagerRunningAs as ;
if ( ( as = manager_running_as_from_string ( optarg ) ) < 0 ) {
log_error ( " Failed to parse running as value %s " , optarg ) ;
return - EINVAL ;
}
running_as = as ;
break ;
}
2010-04-07 02:10:17 +04:00
case ARG_TEST :
action = ACTION_TEST ;
break ;
2010-04-10 19:53:17 +04:00
case ARG_DUMP_CONFIGURATION_ITEMS :
action = ACTION_DUMP_CONFIGURATION_ITEMS ;
break ;
2010-04-13 04:06:27 +04:00
case ARG_CONFIRM_SPAWN :
confirm_spawn = true ;
break ;
2010-04-07 01:40:24 +04:00
case ' h ' :
action = ACTION_HELP ;
break ;
case ' ? ' :
return - EINVAL ;
default :
log_error ( " Unknown option code %c " , c ) ;
return - EINVAL ;
}
return 0 ;
}
static int help ( void ) {
printf ( " %s [options] \n \n "
2010-04-10 19:53:17 +04:00
" -h --help Show this help \n "
" --default=UNIT Set default unit \n "
" --log-level=LEVEL Set log level \n "
" --log-target=TARGET Set log target (console, syslog, kmsg) \n "
" --running-as=AS Set running as (init, system, session) \n "
" --test Determine startup sequence, dump it and exit \n "
2010-04-13 04:06:27 +04:00
" --dump-configuration-items Dump understood unit configuration items \n "
" --confirm-spawn Ask for confirmation when spawning processes \n " ,
2010-04-07 01:40:24 +04:00
__progname ) ;
return 0 ;
}
2009-11-18 02:42:52 +03:00
int main ( int argc , char * argv [ ] ) {
Manager * m = NULL ;
2010-01-26 23:39:06 +03:00
Unit * target = NULL ;
2009-11-18 02:42:52 +03:00
Job * job = NULL ;
int r , retval = 1 ;
2010-04-05 00:49:26 +04:00
2010-04-07 01:55:42 +04:00
if ( getpid ( ) = = 1 )
running_as = MANAGER_INIT ;
else if ( getuid ( ) = = 0 )
running_as = MANAGER_SYSTEM ;
else
running_as = MANAGER_SESSION ;
2010-04-07 01:40:24 +04:00
if ( set_default_unit ( SPECIAL_DEFAULT_TARGET ) < 0 )
goto finish ;
2009-11-18 02:42:52 +03:00
2010-04-07 01:40:24 +04:00
/* Mount /proc, /sys and friends, so that /proc/cmdline and
* / proc / $ PID / fd is available . */
2010-04-07 05:18:35 +04:00
if ( mount_setup ( ) < 0 )
goto finish ;
2010-04-06 23:59:25 +04:00
/* Reset all signal handlers. */
assert_se ( reset_all_signal_handlers ( ) = = 0 ) ;
2010-04-13 04:01:28 +04:00
/* If we are init, we can block sigkill. Yay. */
signal ( SIGKILL , SIG_IGN ) ;
signal ( SIGPIPE , SIG_IGN ) ;
2010-04-07 01:40:24 +04:00
/* Close all open files */
assert_se ( close_all_fds ( NULL , 0 ) = = 0 ) ;
2010-04-07 01:55:42 +04:00
if ( running_as ! = MANAGER_SESSION )
if ( parse_proc_cmdline ( ) < 0 )
goto finish ;
2010-04-07 01:40:24 +04:00
log_parse_environment ( ) ;
if ( parse_argv ( argc , argv ) < 0 )
goto finish ;
if ( action = = ACTION_HELP ) {
retval = help ( ) ;
goto finish ;
2010-04-10 19:53:17 +04:00
} else if ( action = = ACTION_DUMP_CONFIGURATION_ITEMS ) {
unit_dump_config_items ( stdout ) ;
retval = 0 ;
goto finish ;
2010-04-07 01:40:24 +04:00
}
2010-04-07 02:10:17 +04:00
assert_se ( action = = ACTION_RUN | | action = = ACTION_TEST ) ;
2010-04-07 01:40:24 +04:00
2010-04-08 01:24:41 +04:00
/* Set up PATH unless it is already set */
2010-04-10 19:53:17 +04:00
setenv ( " PATH " ,
" /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin " ,
running_as = = MANAGER_INIT ) ;
2010-04-08 01:24:41 +04:00
2010-04-07 01:40:24 +04:00
/* Move out of the way, so that we won't block unmounts */
assert_se ( chdir ( " / " ) = = 0 ) ;
2010-04-13 04:06:27 +04:00
if ( running_as ! = MANAGER_SESSION ) {
/* Become a session leader if we aren't one yet. */
setsid ( ) ;
2010-04-06 23:59:25 +04:00
2010-04-13 04:06:27 +04:00
/* Disable the umask logic */
umask ( 0 ) ;
}
if ( running_as = = MANAGER_INIT )
console_setup ( ) ;
2010-04-06 23:59:25 +04:00
/* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */
dbus_connection_set_change_sigpipe ( FALSE ) ;
2010-04-10 23:42:55 +04:00
/* Open the logging devices, if possible and necessary */
2010-04-06 23:59:25 +04:00
log_open_syslog ( ) ;
log_open_kmsg ( ) ;
2010-04-13 04:00:30 +04:00
/* Make sure we leave a core dump without panicing the
* kernel . */
2010-04-11 00:35:37 +04:00
if ( getpid ( ) = = 1 )
install_crash_handler ( ) ;
2010-04-10 23:40:40 +04:00
2010-04-07 01:55:42 +04:00
log_debug ( " systemd running in %s mode. " , manager_running_as_to_string ( running_as ) ) ;
2010-04-10 07:00:38 +04:00
if ( running_as = = MANAGER_INIT )
2010-04-10 19:53:17 +04:00
hostname_setup ( ) ;
2010-04-10 07:00:38 +04:00
2010-04-13 04:06:27 +04:00
if ( ( r = manager_new ( running_as , confirm_spawn , & m ) ) < 0 ) {
2010-03-31 18:29:55 +04:00
log_error ( " Failed to allocate manager object: %s " , strerror ( - r ) ) ;
2009-11-18 02:42:52 +03:00
goto finish ;
}
2010-01-29 05:18:09 +03:00
if ( ( r = manager_coldplug ( m ) ) < 0 ) {
log_error ( " Failed to retrieve coldplug information: %s " , strerror ( - r ) ) ;
goto finish ;
}
2010-04-05 00:49:26 +04:00
log_debug ( " Activating default unit: %s " , default_unit ) ;
if ( ( r = manager_load_unit ( m , default_unit , & target ) ) < 0 ) {
2010-01-26 21:06:50 +03:00
log_error ( " Failed to load default target: %s " , strerror ( - r ) ) ;
2010-04-08 04:00:40 +04:00
log_info ( " Trying to load rescue target... " ) ;
if ( ( r = manager_load_unit ( m , SPECIAL_RESCUE_TARGET , & target ) ) < 0 ) {
log_error ( " Failed to load rescue target: %s " , strerror ( - r ) ) ;
goto finish ;
}
2009-11-18 02:42:52 +03:00
}
2010-04-07 02:10:17 +04:00
if ( action = = ACTION_TEST ) {
printf ( " → By units: \n " ) ;
manager_dump_units ( m , stdout , " \t " ) ;
}
2010-01-28 04:44:47 +03:00
2010-01-27 02:20:21 +03:00
if ( ( r = manager_add_job ( m , JOB_START , target , JOB_REPLACE , false , & job ) ) < 0 ) {
log_error ( " Failed to start default target: %s " , strerror ( - r ) ) ;
goto finish ;
}
2010-01-19 06:15:20 +03:00
2010-04-07 02:10:17 +04:00
if ( action = = ACTION_TEST ) {
printf ( " → By jobs: \n " ) ;
manager_dump_jobs ( m , stdout , " \t " ) ;
if ( getpid ( ) = = 1 )
pause ( ) ;
retval = 0 ;
goto finish ;
}
2010-01-20 06:02:39 +03:00
2010-01-29 06:26:30 +03:00
if ( ( r = manager_loop ( m ) ) < 0 ) {
log_error ( " Failed to run mainloop: %s " , strerror ( - r ) ) ;
goto finish ;
}
2010-01-24 02:39:29 +03:00
2009-11-18 02:42:52 +03:00
retval = 0 ;
2010-04-07 01:40:24 +04:00
log_debug ( " Exit. " ) ;
2009-11-18 02:42:52 +03:00
finish :
if ( m )
manager_free ( m ) ;
2010-04-07 01:40:24 +04:00
free ( default_unit ) ;
2010-01-27 06:36:30 +03:00
2010-02-01 05:33:24 +03:00
dbus_shutdown ( ) ;
2009-11-18 02:42:52 +03:00
return retval ;
}