2010-08-14 19:59:25 +02:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-01-23 01:52:57 +01:00
2010-02-03 13:03:47 +01:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 00:20:58 +02:00
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
2010-02-03 13:03:47 +01:00
( 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
2012-04-12 00:20:58 +02:00
Lesser General Public License for more details .
2010-02-03 13:03:47 +01:00
2012-04-12 00:20:58 +02:00
You should have received a copy of the GNU Lesser General Public License
2010-02-03 13:03:47 +01:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2010-01-23 01:52:57 +01:00
# include <assert.h>
2010-01-26 04:18:44 +01:00
# include <dirent.h>
# include <errno.h>
# include <fcntl.h>
# include <unistd.h>
2010-01-26 07:02:51 +01:00
# include <string.h>
2010-01-27 06:17:51 +01:00
# include <signal.h>
2010-01-28 02:06:20 +01:00
# include <sys/socket.h>
# include <sys/un.h>
2010-01-30 01:55:42 +01:00
# include <sys/prctl.h>
2010-02-02 13:56:37 +01:00
# include <linux/sched.h>
2010-02-12 02:00:18 +01:00
# include <sys/types.h>
# include <sys/stat.h>
2010-02-14 22:43:08 +01:00
# include <grp.h>
# include <pwd.h>
2010-04-21 22:15:06 +02:00
# include <sys/mount.h>
2010-04-24 05:05:01 +02:00
# include <linux/fs.h>
2010-08-31 01:33:39 +02:00
# include <linux/oom.h>
2012-04-24 14:28:00 +02:00
# include <sys/poll.h>
2013-01-02 12:41:52 +01:00
# include <glob.h>
2014-02-19 02:15:24 +01:00
# include <sys/personality.h>
2013-03-20 06:38:28 +01:00
# include <libgen.h>
2013-12-07 03:29:55 +01:00
# undef basename
2010-01-23 01:52:57 +01:00
2010-06-16 21:54:17 +02:00
# ifdef HAVE_PAM
# include <security/pam_appl.h>
# endif
2014-02-06 10:05:16 +01:00
# ifdef HAVE_SELINUX
# include <selinux/selinux.h>
# endif
2014-02-12 18:28:21 +01:00
# ifdef HAVE_SECCOMP
# include <seccomp.h>
# endif
2014-02-20 16:19:44 +01:00
# ifdef HAVE_APPARMOR
# include <sys/apparmor.h>
# endif
2010-01-23 01:52:57 +01:00
# include "execute.h"
# include "strv.h"
# include "macro.h"
2012-04-10 13:39:02 +02:00
# include "capability.h"
2010-01-23 01:52:57 +01:00
# include "util.h"
2010-01-27 04:31:52 +01:00
# include "log.h"
2012-12-05 11:59:05 +01:00
# include "sd-messages.h"
2010-01-29 20:46:22 +01:00
# include "ioprio.h"
2010-01-30 01:55:42 +01:00
# include "securebits.h"
2010-04-21 22:15:06 +02:00
# include "namespace.h"
2010-08-19 03:18:49 +02:00
# include "exit-status.h"
2010-08-31 01:33:39 +02:00
# include "missing.h"
2010-10-08 16:06:23 +02:00
# include "utmp-wtmp.h"
2011-03-17 04:02:35 +01:00
# include "def.h"
2012-05-07 21:36:12 +02:00
# include "path-util.h"
2013-02-11 03:46:08 +01:00
# include "env-util.h"
2013-02-14 12:26:13 +01:00
# include "fileio.h"
2013-06-27 04:14:27 +02:00
# include "unit.h"
2013-09-16 22:50:38 +02:00
# include "async.h"
2014-02-06 10:05:18 +01:00
# include "selinux-util.h"
2014-02-12 18:28:21 +01:00
# include "errno-list.h"
2014-02-25 20:37:03 +01:00
# include "af-list.h"
2014-03-03 17:14:07 +01:00
# include "mkdir.h"
2014-02-20 16:19:44 +01:00
# include "apparmor-util.h"
2014-11-24 12:46:20 +01:00
# include "smack-util.h"
2014-08-22 19:02:03 +02:00
# include "bus-kernel.h"
2014-07-24 10:40:28 +02:00
# include "label.h"
2014-12-10 03:16:14 +01:00
# include "cap-list.h"
2010-01-23 01:52:57 +01:00
2014-02-13 00:24:00 +01:00
# ifdef HAVE_SECCOMP
# include "seccomp-util.h"
# endif
2012-05-24 02:22:35 +02:00
# define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
systemd: do not output status messages once gettys are running
Make Type=idle communication bidirectional: when bootup is finished,
the manager, as before, signals idling Type=idle jobs to continue.
However, if the boot takes too long, idling jobs signal the manager
that they have had enough, wait a tiny bit more, and continue, taking
ownership of the console. The manager, when signalled that Type=idle
jobs are done, makes a note and will not write to the console anymore.
This is a cosmetic issue, but quite noticable, so let's just fix it.
Based on Harald Hoyer's patch.
https://bugs.freedesktop.org/show_bug.cgi?id=54247
http://unix.stackexchange.com/questions/51805/systemd-messages-after-starting-login/
2013-07-16 03:34:57 +02:00
# define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
2012-05-22 19:26:13 +02:00
2010-04-13 18:50:43 +02:00
/* This assumes there is a 'tty' group */
# define TTY_MODE 0620
2013-12-16 20:00:09 +01:00
# define SNDBUF_SIZE (8*1024*1024)
2010-01-26 04:18:44 +01:00
static int shift_fds ( int fds [ ] , unsigned n_fds ) {
int start , restart_from ;
if ( n_fds < = 0 )
return 0 ;
2010-04-06 23:35:59 +02:00
/* Modifies the fds array! (sorts it) */
2010-01-26 04:18:44 +01:00
assert ( fds ) ;
start = 0 ;
for ( ; ; ) {
int i ;
restart_from = - 1 ;
for ( i = start ; i < ( int ) n_fds ; i + + ) {
int nfd ;
/* Already at right index? */
if ( fds [ i ] = = i + 3 )
continue ;
if ( ( nfd = fcntl ( fds [ i ] , F_DUPFD , i + 3 ) ) < 0 )
return - errno ;
2014-03-18 19:22:43 +01:00
safe_close ( fds [ i ] ) ;
2010-01-26 04:18:44 +01:00
fds [ i ] = nfd ;
/* Hmm, the fd we wanted isn't free? Then
* let ' s remember that and try again from here */
if ( nfd ! = i + 3 & & restart_from < 0 )
restart_from = i ;
}
if ( restart_from < 0 )
break ;
start = restart_from ;
}
return 0 ;
}
2010-04-13 20:43:02 +02:00
static int flags_fds ( const int fds [ ] , unsigned n_fds , bool nonblock ) {
2010-01-27 06:18:45 +01:00
unsigned i ;
2010-04-06 21:53:39 +02:00
int r ;
2010-01-27 06:18:45 +01:00
if ( n_fds < = 0 )
return 0 ;
assert ( fds ) ;
2010-02-12 02:00:18 +01:00
/* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
2010-01-27 06:18:45 +01:00
for ( i = 0 ; i < n_fds ; i + + ) {
2010-04-06 21:53:39 +02:00
if ( ( r = fd_nonblock ( fds [ i ] , nonblock ) ) < 0 )
return r ;
2010-01-27 06:18:45 +01:00
2010-02-12 02:00:18 +01:00
/* We unconditionally drop FD_CLOEXEC from the fds,
* since after all we want to pass these fds to our
* children */
2010-01-27 06:18:45 +01:00
2010-04-06 21:53:39 +02:00
if ( ( r = fd_cloexec ( fds [ i ] , false ) ) < 0 )
return r ;
2010-01-27 06:18:45 +01:00
}
return 0 ;
}
2013-05-03 04:51:50 +02:00
_pure_ static const char * tty_path ( const ExecContext * context ) {
2010-04-13 02:06:27 +02:00
assert ( context ) ;
if ( context - > tty_path )
return context - > tty_path ;
return " /dev/console " ;
}
2013-11-08 18:11:09 +01:00
static void exec_context_tty_reset ( const ExecContext * context ) {
2011-05-18 01:07:31 +02:00
assert ( context ) ;
if ( context - > tty_vhangup )
terminal_vhangup ( tty_path ( context ) ) ;
if ( context - > tty_reset )
reset_terminal ( tty_path ( context ) ) ;
if ( context - > tty_vt_disallocate & & context - > tty_path )
vt_disallocate ( context - > tty_path ) ;
}
2013-02-28 01:35:47 +01:00
static bool is_terminal_output ( ExecOutput o ) {
return
o = = EXEC_OUTPUT_TTY | |
o = = EXEC_OUTPUT_SYSLOG_AND_CONSOLE | |
o = = EXEC_OUTPUT_KMSG_AND_CONSOLE | |
o = = EXEC_OUTPUT_JOURNAL_AND_CONSOLE ;
}
2010-04-13 02:06:27 +02:00
static int open_null_as ( int flags , int nfd ) {
int fd , r ;
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
assert ( nfd > = 0 ) ;
2010-01-28 02:06:20 +01:00
2013-11-27 20:23:18 +01:00
fd = open ( " /dev/null " , flags | O_NOCTTY ) ;
if ( fd < 0 )
2010-01-28 02:06:20 +01:00
return - errno ;
2010-04-13 02:06:27 +02:00
if ( fd ! = nfd ) {
r = dup2 ( fd , nfd ) < 0 ? - errno : nfd ;
2014-03-18 19:22:43 +01:00
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
} else
r = nfd ;
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
return r ;
2010-01-28 02:06:20 +01:00
}
2012-06-21 22:40:47 +02:00
static int connect_logger_as ( const ExecContext * context , ExecOutput output , const char * ident , const char * unit_id , int nfd ) {
2010-04-13 02:06:27 +02:00
int fd , r ;
2013-03-25 00:59:00 +01:00
union sockaddr_union sa = {
. un . sun_family = AF_UNIX ,
. un . sun_path = " /run/systemd/journal/stdout " ,
} ;
2010-01-28 02:06:20 +01:00
assert ( context ) ;
2010-04-13 02:06:27 +02:00
assert ( output < _EXEC_OUTPUT_MAX ) ;
assert ( ident ) ;
assert ( nfd > = 0 ) ;
2010-01-28 02:06:20 +01:00
2012-01-05 21:39:08 +01:00
fd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
if ( fd < 0 )
2010-04-13 02:06:27 +02:00
return - errno ;
2010-01-28 02:06:20 +01:00
2012-01-05 21:39:08 +01:00
r = connect ( fd , & sa . sa , offsetof ( struct sockaddr_un , sun_path ) + strlen ( sa . un . sun_path ) ) ;
if ( r < 0 ) {
2014-03-18 19:22:43 +01:00
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
return - errno ;
}
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
if ( shutdown ( fd , SHUT_RD ) < 0 ) {
2014-03-18 19:22:43 +01:00
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
return - errno ;
}
2010-01-28 02:06:20 +01:00
2013-12-16 20:00:09 +01:00
fd_inc_sndbuf ( fd , SNDBUF_SIZE ) ;
2010-04-13 02:06:27 +02:00
dprintf ( fd ,
2012-06-21 22:40:47 +02:00
" %s \n "
2010-04-13 02:06:27 +02:00
" %s \n "
" %i \n "
2012-01-05 21:39:08 +01:00
" %i \n "
" %i \n "
" %i \n "
2010-05-16 01:46:35 +02:00
" %i \n " ,
context - > syslog_identifier ? context - > syslog_identifier : ident ,
2012-06-21 22:40:47 +02:00
unit_id ,
2012-01-05 21:39:08 +01:00
context - > syslog_priority ,
! ! context - > syslog_level_prefix ,
output = = EXEC_OUTPUT_SYSLOG | | output = = EXEC_OUTPUT_SYSLOG_AND_CONSOLE ,
output = = EXEC_OUTPUT_KMSG | | output = = EXEC_OUTPUT_KMSG_AND_CONSOLE ,
2013-02-28 01:35:47 +01:00
is_terminal_output ( output ) ) ;
2010-04-13 02:06:27 +02:00
if ( fd ! = nfd ) {
r = dup2 ( fd , nfd ) < 0 ? - errno : nfd ;
2014-03-18 19:22:43 +01:00
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
} else
r = nfd ;
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
return r ;
}
static int open_terminal_as ( const char * path , mode_t mode , int nfd ) {
int fd , r ;
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
assert ( path ) ;
assert ( nfd > = 0 ) ;
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
if ( ( fd = open_terminal ( path , mode | O_NOCTTY ) ) < 0 )
return fd ;
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
if ( fd ! = nfd ) {
r = dup2 ( fd , nfd ) < 0 ? - errno : nfd ;
2014-03-18 19:22:43 +01:00
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
} else
r = nfd ;
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
return r ;
}
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
static bool is_terminal_input ( ExecInput i ) {
return
i = = EXEC_INPUT_TTY | |
i = = EXEC_INPUT_TTY_FORCE | |
i = = EXEC_INPUT_TTY_FAIL ;
}
2010-01-28 02:06:20 +01:00
2010-07-08 04:09:17 +02:00
static int fixup_input ( ExecInput std_input , int socket_fd , bool apply_tty_stdin ) {
if ( is_terminal_input ( std_input ) & & ! apply_tty_stdin )
return EXEC_INPUT_NULL ;
2010-01-28 02:06:20 +01:00
2010-05-19 21:50:34 +02:00
if ( std_input = = EXEC_INPUT_SOCKET & & socket_fd < 0 )
2010-04-15 06:19:54 +02:00
return EXEC_INPUT_NULL ;
2010-05-19 21:50:34 +02:00
return std_input ;
2010-04-15 06:19:54 +02:00
}
2010-05-19 21:50:34 +02:00
static int fixup_output ( ExecOutput std_output , int socket_fd ) {
2010-04-15 06:19:54 +02:00
2010-05-19 21:50:34 +02:00
if ( std_output = = EXEC_OUTPUT_SOCKET & & socket_fd < 0 )
2010-04-15 06:19:54 +02:00
return EXEC_OUTPUT_INHERIT ;
2010-05-19 21:50:34 +02:00
return std_output ;
2010-04-15 06:19:54 +02:00
}
2010-07-08 04:09:17 +02:00
static int setup_input ( const ExecContext * context , int socket_fd , bool apply_tty_stdin ) {
2010-04-15 06:19:54 +02:00
ExecInput i ;
assert ( context ) ;
2010-07-08 04:09:17 +02:00
i = fixup_input ( context - > std_input , socket_fd , apply_tty_stdin ) ;
2010-04-15 06:19:54 +02:00
switch ( i ) {
2010-01-28 02:06:20 +01:00
2010-04-13 02:06:27 +02:00
case EXEC_INPUT_NULL :
return open_null_as ( O_RDONLY , STDIN_FILENO ) ;
case EXEC_INPUT_TTY :
case EXEC_INPUT_TTY_FORCE :
case EXEC_INPUT_TTY_FAIL : {
int fd , r ;
2010-01-28 02:06:20 +01:00
2013-08-28 14:01:30 +02:00
fd = acquire_terminal ( tty_path ( context ) ,
i = = EXEC_INPUT_TTY_FAIL ,
i = = EXEC_INPUT_TTY_FORCE ,
false ,
2014-07-29 12:23:31 +02:00
USEC_INFINITY ) ;
2013-08-28 14:01:30 +02:00
if ( fd < 0 )
2010-04-13 02:06:27 +02:00
return fd ;
if ( fd ! = STDIN_FILENO ) {
r = dup2 ( fd , STDIN_FILENO ) < 0 ? - errno : STDIN_FILENO ;
2014-03-18 19:22:43 +01:00
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
} else
r = STDIN_FILENO ;
return r ;
}
2010-04-15 06:19:54 +02:00
case EXEC_INPUT_SOCKET :
return dup2 ( socket_fd , STDIN_FILENO ) < 0 ? - errno : STDIN_FILENO ;
2010-04-13 02:06:27 +02:00
default :
assert_not_reached ( " Unknown input type " ) ;
}
}
2013-02-15 23:36:23 +01:00
static int setup_output ( const ExecContext * context , int fileno , int socket_fd , const char * ident , const char * unit_id , bool apply_tty_stdin ) {
2010-04-15 06:19:54 +02:00
ExecOutput o ;
ExecInput i ;
2013-02-15 22:43:23 +01:00
int r ;
2010-04-15 06:19:54 +02:00
2010-04-13 02:06:27 +02:00
assert ( context ) ;
assert ( ident ) ;
2010-07-08 04:09:17 +02:00
i = fixup_input ( context - > std_input , socket_fd , apply_tty_stdin ) ;
2010-05-19 21:50:34 +02:00
o = fixup_output ( context - > std_output , socket_fd ) ;
2010-04-15 06:19:54 +02:00
2013-02-15 23:36:23 +01:00
if ( fileno = = STDERR_FILENO ) {
ExecOutput e ;
e = fixup_output ( context - > std_error , socket_fd ) ;
2010-04-13 02:06:27 +02:00
2013-02-15 23:36:23 +01:00
/* This expects the input and output are already set up */
/* Don't change the stderr file descriptor if we inherit all
* the way and are not on a tty */
if ( e = = EXEC_OUTPUT_INHERIT & &
o = = EXEC_OUTPUT_INHERIT & &
i = = EXEC_INPUT_NULL & &
! is_terminal_input ( context - > std_input ) & &
getppid ( ) ! = 1 )
return fileno ;
/* Duplicate from stdout if possible */
if ( e = = o | | e = = EXEC_OUTPUT_INHERIT )
return dup2 ( STDOUT_FILENO , fileno ) < 0 ? - errno : fileno ;
2010-01-28 02:06:20 +01:00
2013-02-15 23:36:23 +01:00
o = e ;
2010-04-13 02:06:27 +02:00
2013-02-15 23:36:23 +01:00
} else if ( o = = EXEC_OUTPUT_INHERIT ) {
2010-07-12 22:04:59 +02:00
/* If input got downgraded, inherit the original value */
if ( i = = EXEC_INPUT_NULL & & is_terminal_input ( context - > std_input ) )
2013-02-15 23:36:23 +01:00
return open_terminal_as ( tty_path ( context ) , O_WRONLY , fileno ) ;
2010-07-12 22:04:59 +02:00
2010-07-07 04:37:42 +02:00
/* If the input is connected to anything that's not a /dev/null, inherit that... */
2010-05-20 01:08:13 +02:00
if ( i ! = EXEC_INPUT_NULL )
2013-02-15 23:36:23 +01:00
return dup2 ( STDIN_FILENO , fileno ) < 0 ? - errno : fileno ;
2010-01-28 02:06:20 +01:00
2010-07-07 04:37:42 +02:00
/* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
if ( getppid ( ) ! = 1 )
2013-02-15 23:36:23 +01:00
return fileno ;
2010-01-30 01:55:42 +01:00
2013-02-15 23:36:23 +01:00
/* We need to open /dev/null here anew, to get the right access mode. */
return open_null_as ( O_WRONLY , fileno ) ;
2010-01-28 02:06:20 +01:00
}
2010-01-30 01:55:42 +01:00
2013-02-15 23:36:23 +01:00
switch ( o ) {
2010-04-13 02:06:27 +02:00
case EXEC_OUTPUT_NULL :
2013-02-15 23:36:23 +01:00
return open_null_as ( O_WRONLY , fileno ) ;
2010-04-13 02:06:27 +02:00
case EXEC_OUTPUT_TTY :
2010-04-15 06:19:54 +02:00
if ( is_terminal_input ( i ) )
2013-02-15 23:36:23 +01:00
return dup2 ( STDIN_FILENO , fileno ) < 0 ? - errno : fileno ;
2010-04-13 02:06:27 +02:00
/* We don't reset the terminal if this is just about output */
2013-02-15 23:36:23 +01:00
return open_terminal_as ( tty_path ( context ) , O_WRONLY , fileno ) ;
2010-04-13 02:06:27 +02:00
case EXEC_OUTPUT_SYSLOG :
2011-02-15 01:27:53 +01:00
case EXEC_OUTPUT_SYSLOG_AND_CONSOLE :
2010-05-19 21:49:03 +02:00
case EXEC_OUTPUT_KMSG :
2011-02-15 01:27:53 +01:00
case EXEC_OUTPUT_KMSG_AND_CONSOLE :
2012-01-05 23:54:45 +01:00
case EXEC_OUTPUT_JOURNAL :
case EXEC_OUTPUT_JOURNAL_AND_CONSOLE :
2013-02-15 23:36:23 +01:00
r = connect_logger_as ( context , o , ident , unit_id , fileno ) ;
2013-02-15 22:43:23 +01:00
if ( r < 0 ) {
2014-11-28 02:05:14 +01:00
log_unit_struct ( unit_id ,
LOG_CRIT ,
LOG_MESSAGE ( " Failed to connect %s of %s to the journal socket: %s " ,
fileno = = STDOUT_FILENO ? " stdout " : " stderr " ,
unit_id , strerror ( - r ) ) ,
LOG_ERRNO ( - r ) ,
NULL ) ;
2013-02-15 23:36:23 +01:00
r = open_null_as ( O_WRONLY , fileno ) ;
2013-02-15 22:43:23 +01:00
}
return r ;
2010-04-15 06:19:54 +02:00
case EXEC_OUTPUT_SOCKET :
assert ( socket_fd > = 0 ) ;
2013-02-15 23:36:23 +01:00
return dup2 ( socket_fd , fileno ) < 0 ? - errno : fileno ;
2010-01-30 01:55:42 +01:00
default :
2010-04-13 02:06:27 +02:00
assert_not_reached ( " Unknown error type " ) ;
2010-01-30 01:55:42 +01:00
}
2010-01-28 02:06:20 +01:00
}
2010-04-13 18:50:43 +02:00
static int chown_terminal ( int fd , uid_t uid ) {
struct stat st ;
assert ( fd > = 0 ) ;
/* This might fail. What matters are the results. */
2010-05-10 03:34:31 +02:00
( void ) fchown ( fd , uid , - 1 ) ;
( void ) fchmod ( fd , TTY_MODE ) ;
2010-04-13 18:50:43 +02:00
if ( fstat ( fd , & st ) < 0 )
return - errno ;
2010-04-13 21:13:49 +02:00
if ( st . st_uid ! = uid | | ( st . st_mode & 0777 ) ! = TTY_MODE )
2010-04-13 18:50:43 +02:00
return - EPERM ;
return 0 ;
}
2012-06-26 12:16:18 +02:00
static int setup_confirm_stdio ( int * _saved_stdin ,
2010-04-13 02:06:27 +02:00
int * _saved_stdout ) {
int fd = - 1 , saved_stdin , saved_stdout = - 1 , r ;
assert ( _saved_stdin ) ;
assert ( _saved_stdout ) ;
2012-06-26 12:16:18 +02:00
saved_stdin = fcntl ( STDIN_FILENO , F_DUPFD , 3 ) ;
if ( saved_stdin < 0 )
return - errno ;
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
saved_stdout = fcntl ( STDOUT_FILENO , F_DUPFD , 3 ) ;
if ( saved_stdout < 0 ) {
r = errno ;
2010-04-13 02:06:27 +02:00
goto fail ;
}
2012-06-26 12:16:18 +02:00
fd = acquire_terminal (
" /dev/console " ,
false ,
false ,
false ,
DEFAULT_CONFIRM_USEC ) ;
if ( fd < 0 ) {
r = fd ;
2010-04-13 02:06:27 +02:00
goto fail ;
}
2012-06-26 12:16:18 +02:00
r = chown_terminal ( fd , getuid ( ) ) ;
if ( r < 0 )
2010-04-13 18:50:43 +02:00
goto fail ;
2010-04-13 02:06:27 +02:00
if ( dup2 ( fd , STDIN_FILENO ) < 0 ) {
2012-06-26 12:16:18 +02:00
r = - errno ;
2010-04-13 02:06:27 +02:00
goto fail ;
}
if ( dup2 ( fd , STDOUT_FILENO ) < 0 ) {
2012-06-26 12:16:18 +02:00
r = - errno ;
2010-04-13 02:06:27 +02:00
goto fail ;
}
if ( fd > = 2 )
2014-03-18 19:22:43 +01:00
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
* _saved_stdin = saved_stdin ;
* _saved_stdout = saved_stdout ;
return 0 ;
fail :
2014-03-18 19:22:43 +01:00
safe_close ( saved_stdout ) ;
safe_close ( saved_stdin ) ;
safe_close ( fd ) ;
2010-04-13 02:06:27 +02:00
return r ;
}
2013-10-16 03:17:09 +02:00
_printf_ ( 1 , 2 ) static int write_confirm_message ( const char * format , . . . ) {
2014-03-18 19:22:43 +01:00
_cleanup_close_ int fd = - 1 ;
2012-06-26 12:16:18 +02:00
va_list ap ;
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
assert ( format ) ;
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
fd = open_terminal ( " /dev/console " , O_WRONLY | O_NOCTTY | O_CLOEXEC ) ;
if ( fd < 0 )
return fd ;
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
va_start ( ap , format ) ;
vdprintf ( fd , format , ap ) ;
va_end ( ap ) ;
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
return 0 ;
}
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
static int restore_confirm_stdio ( int * saved_stdin ,
int * saved_stdout ) {
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
int r = 0 ;
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
assert ( saved_stdin ) ;
assert ( saved_stdout ) ;
release_terminal ( ) ;
if ( * saved_stdin > = 0 )
2010-04-13 02:06:27 +02:00
if ( dup2 ( * saved_stdin , STDIN_FILENO ) < 0 )
2012-06-26 12:16:18 +02:00
r = - errno ;
2010-04-13 02:06:27 +02:00
2012-06-26 12:16:18 +02:00
if ( * saved_stdout > = 0 )
2010-04-13 02:06:27 +02:00
if ( dup2 ( * saved_stdout , STDOUT_FILENO ) < 0 )
2012-06-26 12:16:18 +02:00
r = - errno ;
2010-04-13 02:06:27 +02:00
2014-03-18 19:22:43 +01:00
safe_close ( * saved_stdin ) ;
safe_close ( * saved_stdout ) ;
2012-06-26 12:16:18 +02:00
return r ;
}
static int ask_for_confirmation ( char * response , char * * argv ) {
int saved_stdout = - 1 , saved_stdin = - 1 , r ;
2014-06-24 19:00:32 +02:00
_cleanup_free_ char * line = NULL ;
2012-06-26 12:16:18 +02:00
r = setup_confirm_stdio ( & saved_stdin , & saved_stdout ) ;
if ( r < 0 )
return r ;
line = exec_command_line ( argv ) ;
if ( ! line )
return - ENOMEM ;
2014-07-07 15:05:37 +02:00
r = ask_char ( response , " yns " , " Execute %s? [Yes, No, Skip] " , line ) ;
2012-06-26 12:16:18 +02:00
restore_confirm_stdio ( & saved_stdin , & saved_stdout ) ;
return r ;
2010-04-13 02:06:27 +02:00
}
2010-02-14 22:43:08 +01:00
static int enforce_groups ( const ExecContext * context , const char * username , gid_t gid ) {
bool keep_groups = false ;
int r ;
assert ( context ) ;
2011-02-21 15:32:17 +01:00
/* Lookup and set GID and supplementary group list. Here too
2010-02-14 22:43:08 +01:00
* we avoid NSS lookups for gid = 0. */
if ( context - > group | | username ) {
2011-07-23 01:17:59 +02:00
if ( context - > group ) {
const char * g = context - > group ;
if ( ( r = get_group_creds ( & g , & gid ) ) < 0 )
2010-02-14 22:43:08 +01:00
return r ;
2011-07-23 01:17:59 +02:00
}
2010-02-14 22:43:08 +01:00
/* First step, initialize groups from /etc/groups */
if ( username & & gid ! = 0 ) {
if ( initgroups ( username , gid ) < 0 )
return - errno ;
keep_groups = true ;
}
/* Second step, set our gids */
if ( setresgid ( gid , gid , gid ) < 0 )
return - errno ;
}
if ( context - > supplementary_groups ) {
int ngroups_max , k ;
gid_t * gids ;
char * * i ;
/* Final step, initialize any manually set supplementary groups */
2011-03-31 15:35:40 +02:00
assert_se ( ( ngroups_max = ( int ) sysconf ( _SC_NGROUPS_MAX ) ) > 0 ) ;
2010-02-14 22:43:08 +01:00
if ( ! ( gids = new ( gid_t , ngroups_max ) ) )
return - ENOMEM ;
if ( keep_groups ) {
if ( ( k = getgroups ( ngroups_max , gids ) ) < 0 ) {
free ( gids ) ;
return - errno ;
}
} else
k = 0 ;
STRV_FOREACH ( i , context - > supplementary_groups ) {
2011-07-23 01:17:59 +02:00
const char * g ;
2010-02-14 22:43:08 +01:00
if ( k > = ngroups_max ) {
free ( gids ) ;
return - E2BIG ;
}
2011-07-23 01:17:59 +02:00
g = * i ;
r = get_group_creds ( & g , gids + k ) ;
if ( r < 0 ) {
2010-02-14 22:43:08 +01:00
free ( gids ) ;
return r ;
}
k + + ;
}
if ( setgroups ( k , gids ) < 0 ) {
free ( gids ) ;
return - errno ;
}
free ( gids ) ;
}
return 0 ;
}
static int enforce_user ( const ExecContext * context , uid_t uid ) {
assert ( context ) ;
/* Sets (but doesn't lookup) the uid and make sure we keep the
* capabilities while doing so . */
if ( context - > capabilities ) {
2014-01-01 04:35:54 +01:00
_cleanup_cap_free_ cap_t d = NULL ;
2010-02-14 22:43:08 +01:00
static const cap_value_t bits [ ] = {
CAP_SETUID , /* Necessary so that we can run setresuid() below */
CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
} ;
/* First step: If we need to keep capabilities but
* drop privileges we need to make sure we keep our
2013-03-30 06:40:11 +01:00
* caps , while we drop privileges . */
2010-03-31 16:25:33 +02:00
if ( uid ! = 0 ) {
2013-03-30 06:40:11 +01:00
int sb = context - > secure_bits | 1 < < SECURE_KEEP_CAPS ;
2010-03-31 16:25:33 +02:00
if ( prctl ( PR_GET_SECUREBITS ) ! = sb )
if ( prctl ( PR_SET_SECUREBITS , sb ) < 0 )
return - errno ;
}
2010-02-14 22:43:08 +01:00
2011-02-21 15:32:17 +01:00
/* Second step: set the capabilities. This will reduce
2010-02-14 22:43:08 +01:00
* the capabilities to the minimum we need . */
2014-01-01 04:35:54 +01:00
d = cap_dup ( context - > capabilities ) ;
if ( ! d )
2010-02-14 22:43:08 +01:00
return - errno ;
if ( cap_set_flag ( d , CAP_EFFECTIVE , ELEMENTSOF ( bits ) , bits , CAP_SET ) < 0 | |
2014-01-01 04:35:54 +01:00
cap_set_flag ( d , CAP_PERMITTED , ELEMENTSOF ( bits ) , bits , CAP_SET ) < 0 )
return - errno ;
2010-02-14 22:43:08 +01:00
2014-01-01 04:35:54 +01:00
if ( cap_set_proc ( d ) < 0 )
return - errno ;
2010-02-14 22:43:08 +01:00
}
/* Third step: actually set the uids */
if ( setresuid ( uid , uid , uid ) < 0 )
return - errno ;
/* At this point we should have all necessary capabilities but
are otherwise a normal user . However , the caps might got
corrupted due to the setresuid ( ) so we need clean them up
later . This is done outside of this call . */
return 0 ;
}
2010-06-16 21:54:17 +02:00
# ifdef HAVE_PAM
static int null_conv (
int num_msg ,
const struct pam_message * * msg ,
struct pam_response * * resp ,
void * appdata_ptr ) {
/* We don't support conversations */
return PAM_CONV_ERR ;
}
static int setup_pam (
const char * name ,
const char * user ,
2012-05-17 21:17:42 +02:00
uid_t uid ,
2010-06-16 21:54:17 +02:00
const char * tty ,
char * * * pam_env ,
int fds [ ] , unsigned n_fds ) {
static const struct pam_conv conv = {
. conv = null_conv ,
. appdata_ptr = NULL
} ;
pam_handle_t * handle = NULL ;
sigset_t ss , old_ss ;
int pam_code = PAM_SUCCESS ;
2011-11-17 00:16:22 +01:00
int err ;
2010-06-16 21:54:17 +02:00
char * * e = NULL ;
bool close_session = false ;
pid_t pam_pid = 0 , parent_pid ;
2013-08-28 14:01:30 +02:00
int flags = 0 ;
2010-06-16 21:54:17 +02:00
assert ( name ) ;
assert ( user ) ;
assert ( pam_env ) ;
/* We set up PAM in the parent process, then fork. The child
2011-02-21 15:32:17 +01:00
* will then stay around until killed via PR_GET_PDEATHSIG or
2010-06-16 21:54:17 +02:00
* systemd via the cgroup logic . It will then remove the PAM
* session again . The parent process will exec ( ) the actual
* daemon . We do things this way to ensure that the main PID
* of the daemon is the one we initially fork ( ) ed . */
2013-08-28 14:01:30 +02:00
if ( log_get_max_level ( ) < LOG_PRI ( LOG_DEBUG ) )
flags | = PAM_SILENT ;
2013-08-28 13:54:43 +02:00
pam_code = pam_start ( name , user , & conv , & handle ) ;
if ( pam_code ! = PAM_SUCCESS ) {
2010-06-16 21:54:17 +02:00
handle = NULL ;
goto fail ;
}
2013-08-28 13:54:43 +02:00
if ( tty ) {
pam_code = pam_set_item ( handle , PAM_TTY , tty ) ;
if ( pam_code ! = PAM_SUCCESS )
2010-06-16 21:54:17 +02:00
goto fail ;
2013-08-28 13:54:43 +02:00
}
2010-06-16 21:54:17 +02:00
2013-08-28 14:01:30 +02:00
pam_code = pam_acct_mgmt ( handle , flags ) ;
2013-08-28 13:54:43 +02:00
if ( pam_code ! = PAM_SUCCESS )
2010-06-16 21:54:17 +02:00
goto fail ;
2013-08-28 14:01:30 +02:00
pam_code = pam_open_session ( handle , flags ) ;
2013-08-28 13:54:43 +02:00
if ( pam_code ! = PAM_SUCCESS )
2010-06-16 21:54:17 +02:00
goto fail ;
close_session = true ;
2013-08-28 13:54:43 +02:00
e = pam_getenvlist ( handle ) ;
if ( ! e ) {
2010-06-16 21:54:17 +02:00
pam_code = PAM_BUF_ERR ;
goto fail ;
}
/* Block SIGTERM, so that we know that it won't get lost in
* the child */
if ( sigemptyset ( & ss ) < 0 | |
sigaddset ( & ss , SIGTERM ) < 0 | |
sigprocmask ( SIG_BLOCK , & ss , & old_ss ) < 0 )
goto fail ;
parent_pid = getpid ( ) ;
2013-08-28 13:54:43 +02:00
pam_pid = fork ( ) ;
if ( pam_pid < 0 )
2010-06-16 21:54:17 +02:00
goto fail ;
if ( pam_pid = = 0 ) {
int sig ;
int r = EXIT_PAM ;
/* The child's job is to reset the PAM session on
* termination */
/* This string must fit in 10 chars (i.e. the length
2012-02-01 22:33:15 +01:00
* of " /sbin/init " ) , to look pretty in / bin / ps */
rename_process ( " (sd-pam) " ) ;
2010-06-16 21:54:17 +02:00
/* Make sure we don't keep open the passed fds in this
child . We assume that otherwise only those fds are
open here that have been opened by PAM . */
close_many ( fds , n_fds ) ;
2012-05-17 21:17:42 +02:00
/* Drop privileges - we don't need any to pam_close_session
* and this will make PR_SET_PDEATHSIG work in most cases .
* If this fails , ignore the error - but expect sd - pam threads
* to fail to exit normally */
if ( setresuid ( uid , uid , uid ) < 0 )
2014-11-28 13:19:16 +01:00
log_error_errno ( r , " Error: Failed to setresuid() in sd-pam: %m " ) ;
2012-05-17 21:17:42 +02:00
/* Wait until our parent died. This will only work if
* the above setresuid ( ) succeeds , otherwise the kernel
* will not allow unprivileged parents kill their privileged
* children this way . We rely on the control groups kill logic
2010-06-16 21:54:17 +02:00
* to do the rest for us . */
if ( prctl ( PR_SET_PDEATHSIG , SIGTERM ) < 0 )
goto child_finish ;
/* Check if our parent process might already have
* died ? */
if ( getppid ( ) = = parent_pid ) {
2011-06-30 04:15:39 +02:00
for ( ; ; ) {
if ( sigwait ( & ss , & sig ) < 0 ) {
if ( errno = = EINTR )
continue ;
goto child_finish ;
}
2010-06-16 21:54:17 +02:00
2011-06-30 04:15:39 +02:00
assert ( sig = = SIGTERM ) ;
break ;
}
2010-06-16 21:54:17 +02:00
}
2011-06-30 04:15:39 +02:00
/* If our parent died we'll end the session */
2013-08-28 13:54:43 +02:00
if ( getppid ( ) ! = parent_pid ) {
2013-08-28 14:01:30 +02:00
pam_code = pam_close_session ( handle , flags ) ;
2013-08-28 13:54:43 +02:00
if ( pam_code ! = PAM_SUCCESS )
2010-06-16 21:54:17 +02:00
goto child_finish ;
2013-08-28 13:54:43 +02:00
}
2010-06-16 21:54:17 +02:00
r = 0 ;
child_finish :
2013-08-28 14:01:30 +02:00
pam_end ( handle , pam_code | flags ) ;
2010-06-16 21:54:17 +02:00
_exit ( r ) ;
}
/* If the child was forked off successfully it will do all the
* cleanups , so forget about the handle here . */
handle = NULL ;
2011-06-30 02:15:01 +02:00
/* Unblock SIGTERM again in the parent */
2010-06-16 21:54:17 +02:00
if ( sigprocmask ( SIG_SETMASK , & old_ss , NULL ) < 0 )
goto fail ;
/* We close the log explicitly here, since the PAM modules
* might have opened it , but we don ' t want this fd around . */
closelog ( ) ;
2011-06-30 04:31:34 +02:00
* pam_env = e ;
e = NULL ;
2010-06-16 21:54:17 +02:00
return 0 ;
fail :
2013-08-28 14:01:30 +02:00
if ( pam_code ! = PAM_SUCCESS ) {
log_error ( " PAM failed: %s " , pam_strerror ( handle , pam_code ) ) ;
2011-11-17 00:16:22 +01:00
err = - EPERM ; /* PAM errors do not map to errno */
2013-08-28 14:01:30 +02:00
} else {
2014-11-28 19:29:59 +01:00
log_error_errno ( errno , " PAM failed: %m " ) ;
2011-11-17 00:16:22 +01:00
err = - errno ;
2013-08-28 14:01:30 +02:00
}
2011-11-17 00:16:22 +01:00
2010-06-16 21:54:17 +02:00
if ( handle ) {
if ( close_session )
2013-08-28 14:01:30 +02:00
pam_code = pam_close_session ( handle , flags ) ;
2010-06-16 21:54:17 +02:00
2013-08-28 14:01:30 +02:00
pam_end ( handle , pam_code | flags ) ;
2010-06-16 21:54:17 +02:00
}
strv_free ( e ) ;
closelog ( ) ;
2011-03-03 23:55:30 +01:00
if ( pam_pid > 1 ) {
2010-06-16 21:54:17 +02:00
kill ( pam_pid , SIGTERM ) ;
2011-03-03 23:55:30 +01:00
kill ( pam_pid , SIGCONT ) ;
}
2010-06-16 21:54:17 +02:00
2011-11-17 00:16:22 +01:00
return err ;
2010-06-16 21:54:17 +02:00
}
# endif
2012-02-01 22:33:15 +01:00
static void rename_process_from_path ( const char * path ) {
char process_name [ 11 ] ;
const char * p ;
size_t l ;
/* This resulting string must fit in 10 chars (i.e. the length
* of " /sbin/init " ) to look pretty in / bin / ps */
2013-12-07 03:29:55 +01:00
p = basename ( path ) ;
2012-02-01 22:33:15 +01:00
if ( isempty ( p ) ) {
rename_process ( " (...) " ) ;
return ;
}
l = strlen ( p ) ;
if ( l > 8 ) {
/* The end of the process name is usually more
* interesting , since the first bit might just be
* " systemd- " */
p = p + l - 8 ;
l = 8 ;
}
process_name [ 0 ] = ' ( ' ;
memcpy ( process_name + 1 , p , l ) ;
process_name [ 1 + l ] = ' ) ' ;
process_name [ 1 + l + 1 ] = 0 ;
rename_process ( process_name ) ;
}
2014-02-12 01:29:54 +01:00
# ifdef HAVE_SECCOMP
2014-02-12 18:28:21 +01:00
2014-09-08 22:10:36 +02:00
static int apply_seccomp ( const ExecContext * c ) {
2014-02-12 18:28:21 +01:00
uint32_t negative_action , action ;
scmp_filter_ctx * seccomp ;
2014-02-12 01:29:54 +01:00
Iterator i ;
void * id ;
2014-02-12 18:28:21 +01:00
int r ;
2012-07-17 04:17:53 +02:00
2014-02-12 01:29:54 +01:00
assert ( c ) ;
2012-07-17 04:17:53 +02:00
2014-02-12 18:28:21 +01:00
negative_action = c - > syscall_errno = = 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO ( c - > syscall_errno ) ;
seccomp = seccomp_init ( c - > syscall_whitelist ? negative_action : SCMP_ACT_ALLOW ) ;
if ( ! seccomp )
return - ENOMEM ;
2012-07-17 04:17:53 +02:00
2014-02-18 22:14:00 +01:00
if ( c - > syscall_archs ) {
SET_FOREACH ( id , c - > syscall_archs , i ) {
r = seccomp_arch_add ( seccomp , PTR_TO_UINT32 ( id ) - 1 ) ;
if ( r = = - EEXIST )
continue ;
2014-02-25 20:32:27 +01:00
if ( r < 0 )
goto finish ;
2014-02-18 22:14:00 +01:00
}
2014-02-25 20:32:27 +01:00
} else {
2014-02-18 22:14:00 +01:00
r = seccomp_add_secondary_archs ( seccomp ) ;
2014-02-25 20:32:27 +01:00
if ( r < 0 )
goto finish ;
2014-02-13 00:24:00 +01:00
}
2012-07-17 04:17:53 +02:00
2014-02-13 00:24:00 +01:00
action = c - > syscall_whitelist ? SCMP_ACT_ALLOW : negative_action ;
2014-02-12 18:28:21 +01:00
SET_FOREACH ( id , c - > syscall_filter , i ) {
r = seccomp_rule_add ( seccomp , action , PTR_TO_INT ( id ) - 1 , 0 ) ;
2014-02-25 20:32:27 +01:00
if ( r < 0 )
goto finish ;
2014-02-12 01:29:54 +01:00
}
2012-07-17 04:17:53 +02:00
2014-02-25 20:32:27 +01:00
r = seccomp_attr_set ( seccomp , SCMP_FLTATR_CTL_NNP , 0 ) ;
if ( r < 0 )
goto finish ;
2014-02-12 18:28:21 +01:00
r = seccomp_load ( seccomp ) ;
2014-02-25 20:32:27 +01:00
finish :
2014-02-12 18:28:21 +01:00
seccomp_release ( seccomp ) ;
2014-02-25 20:37:03 +01:00
return r ;
}
2014-09-08 22:10:36 +02:00
static int apply_address_families ( const ExecContext * c ) {
2014-02-25 20:37:03 +01:00
scmp_filter_ctx * seccomp ;
Iterator i ;
int r ;
assert ( c ) ;
seccomp = seccomp_init ( SCMP_ACT_ALLOW ) ;
if ( ! seccomp )
return - ENOMEM ;
r = seccomp_add_secondary_archs ( seccomp ) ;
if ( r < 0 )
goto finish ;
if ( c - > address_families_whitelist ) {
int af , first = 0 , last = 0 ;
void * afp ;
/* If this is a whitelist, we first block the address
* families that are out of range and then everything
* that is not in the set . First , we find the lowest
* and highest address family in the set . */
SET_FOREACH ( afp , c - > address_families , i ) {
af = PTR_TO_INT ( afp ) ;
2014-02-12 18:28:21 +01:00
2014-02-25 20:37:03 +01:00
if ( af < = 0 | | af > = af_max ( ) )
continue ;
if ( first = = 0 | | af < first )
first = af ;
if ( last = = 0 | | af > last )
last = af ;
}
assert ( ( first = = 0 ) = = ( last = = 0 ) ) ;
if ( first = = 0 ) {
/* No entries in the valid range, block everything */
r = seccomp_rule_add (
seccomp ,
SCMP_ACT_ERRNO ( EPROTONOSUPPORT ) ,
SCMP_SYS ( socket ) ,
0 ) ;
if ( r < 0 )
goto finish ;
} else {
/* Block everything below the first entry */
r = seccomp_rule_add (
seccomp ,
SCMP_ACT_ERRNO ( EPROTONOSUPPORT ) ,
SCMP_SYS ( socket ) ,
1 ,
SCMP_A0 ( SCMP_CMP_LT , first ) ) ;
if ( r < 0 )
goto finish ;
/* Block everything above the last entry */
r = seccomp_rule_add (
seccomp ,
SCMP_ACT_ERRNO ( EPROTONOSUPPORT ) ,
SCMP_SYS ( socket ) ,
1 ,
SCMP_A0 ( SCMP_CMP_GT , last ) ) ;
if ( r < 0 )
goto finish ;
/* Block everything between the first and last
* entry */
for ( af = 1 ; af < af_max ( ) ; af + + ) {
if ( set_contains ( c - > address_families , INT_TO_PTR ( af ) ) )
continue ;
r = seccomp_rule_add (
seccomp ,
SCMP_ACT_ERRNO ( EPROTONOSUPPORT ) ,
SCMP_SYS ( socket ) ,
1 ,
SCMP_A0 ( SCMP_CMP_EQ , af ) ) ;
if ( r < 0 )
goto finish ;
}
}
} else {
void * af ;
/* If this is a blacklist, then generate one rule for
* each address family that are then combined in OR
* checks . */
SET_FOREACH ( af , c - > address_families , i ) {
r = seccomp_rule_add (
seccomp ,
SCMP_ACT_ERRNO ( EPROTONOSUPPORT ) ,
SCMP_SYS ( socket ) ,
1 ,
SCMP_A0 ( SCMP_CMP_EQ , PTR_TO_INT ( af ) ) ) ;
if ( r < 0 )
goto finish ;
}
}
r = seccomp_attr_set ( seccomp , SCMP_FLTATR_CTL_NNP , 0 ) ;
if ( r < 0 )
goto finish ;
r = seccomp_load ( seccomp ) ;
finish :
seccomp_release ( seccomp ) ;
2014-02-12 18:28:21 +01:00
return r ;
2012-07-17 04:17:53 +02:00
}
2014-02-25 20:37:03 +01:00
2014-02-12 01:29:54 +01:00
# endif
2012-07-17 04:17:53 +02:00
systemd: do not output status messages once gettys are running
Make Type=idle communication bidirectional: when bootup is finished,
the manager, as before, signals idling Type=idle jobs to continue.
However, if the boot takes too long, idling jobs signal the manager
that they have had enough, wait a tiny bit more, and continue, taking
ownership of the console. The manager, when signalled that Type=idle
jobs are done, makes a note and will not write to the console anymore.
This is a cosmetic issue, but quite noticable, so let's just fix it.
Based on Harald Hoyer's patch.
https://bugs.freedesktop.org/show_bug.cgi?id=54247
http://unix.stackexchange.com/questions/51805/systemd-messages-after-starting-login/
2013-07-16 03:34:57 +02:00
static void do_idle_pipe_dance ( int idle_pipe [ 4 ] ) {
assert ( idle_pipe ) ;
2014-03-18 19:22:43 +01:00
safe_close ( idle_pipe [ 1 ] ) ;
safe_close ( idle_pipe [ 2 ] ) ;
systemd: do not output status messages once gettys are running
Make Type=idle communication bidirectional: when bootup is finished,
the manager, as before, signals idling Type=idle jobs to continue.
However, if the boot takes too long, idling jobs signal the manager
that they have had enough, wait a tiny bit more, and continue, taking
ownership of the console. The manager, when signalled that Type=idle
jobs are done, makes a note and will not write to the console anymore.
This is a cosmetic issue, but quite noticable, so let's just fix it.
Based on Harald Hoyer's patch.
https://bugs.freedesktop.org/show_bug.cgi?id=54247
http://unix.stackexchange.com/questions/51805/systemd-messages-after-starting-login/
2013-07-16 03:34:57 +02:00
if ( idle_pipe [ 0 ] > = 0 ) {
int r ;
r = fd_wait_for_event ( idle_pipe [ 0 ] , POLLHUP , IDLE_TIMEOUT_USEC ) ;
if ( idle_pipe [ 3 ] > = 0 & & r = = 0 /* timeout */ ) {
/* Signal systemd that we are bored and want to continue. */
write ( idle_pipe [ 3 ] , " x " , 1 ) ;
/* Wait for systemd to react to the signal above. */
fd_wait_for_event ( idle_pipe [ 0 ] , POLLHUP , IDLE_TIMEOUT2_USEC ) ;
}
2014-03-18 19:22:43 +01:00
safe_close ( idle_pipe [ 0 ] ) ;
systemd: do not output status messages once gettys are running
Make Type=idle communication bidirectional: when bootup is finished,
the manager, as before, signals idling Type=idle jobs to continue.
However, if the boot takes too long, idling jobs signal the manager
that they have had enough, wait a tiny bit more, and continue, taking
ownership of the console. The manager, when signalled that Type=idle
jobs are done, makes a note and will not write to the console anymore.
This is a cosmetic issue, but quite noticable, so let's just fix it.
Based on Harald Hoyer's patch.
https://bugs.freedesktop.org/show_bug.cgi?id=54247
http://unix.stackexchange.com/questions/51805/systemd-messages-after-starting-login/
2013-07-16 03:34:57 +02:00
}
2014-03-18 19:22:43 +01:00
safe_close ( idle_pipe [ 3 ] ) ;
systemd: do not output status messages once gettys are running
Make Type=idle communication bidirectional: when bootup is finished,
the manager, as before, signals idling Type=idle jobs to continue.
However, if the boot takes too long, idling jobs signal the manager
that they have had enough, wait a tiny bit more, and continue, taking
ownership of the console. The manager, when signalled that Type=idle
jobs are done, makes a note and will not write to the console anymore.
This is a cosmetic issue, but quite noticable, so let's just fix it.
Based on Harald Hoyer's patch.
https://bugs.freedesktop.org/show_bug.cgi?id=54247
http://unix.stackexchange.com/questions/51805/systemd-messages-after-starting-login/
2013-07-16 03:34:57 +02:00
}
2013-12-18 17:41:16 +01:00
static int build_environment (
2014-08-23 15:28:37 +02:00
const ExecContext * c ,
2013-12-18 17:41:16 +01:00
unsigned n_fds ,
2013-12-22 22:14:05 +01:00
usec_t watchdog_usec ,
2013-12-18 17:41:16 +01:00
const char * home ,
const char * username ,
const char * shell ,
char * * * ret ) {
_cleanup_strv_free_ char * * our_env = NULL ;
unsigned n_env = 0 ;
char * x ;
assert ( c ) ;
assert ( ret ) ;
2013-12-22 22:14:05 +01:00
our_env = new0 ( char * , 10 ) ;
2013-12-18 17:41:16 +01:00
if ( ! our_env )
return - ENOMEM ;
if ( n_fds > 0 ) {
2013-12-30 23:22:26 +01:00
if ( asprintf ( & x , " LISTEN_PID= " PID_FMT , getpid ( ) ) < 0 )
2013-12-18 17:41:16 +01:00
return - ENOMEM ;
our_env [ n_env + + ] = x ;
if ( asprintf ( & x , " LISTEN_FDS=%u " , n_fds ) < 0 )
return - ENOMEM ;
our_env [ n_env + + ] = x ;
}
2013-12-22 22:14:05 +01:00
if ( watchdog_usec > 0 ) {
2013-12-30 23:22:26 +01:00
if ( asprintf ( & x , " WATCHDOG_PID= " PID_FMT , getpid ( ) ) < 0 )
2013-12-22 22:14:05 +01:00
return - ENOMEM ;
our_env [ n_env + + ] = x ;
2014-04-25 13:45:15 +02:00
if ( asprintf ( & x , " WATCHDOG_USEC= " USEC_FMT , watchdog_usec ) < 0 )
2013-12-22 22:14:05 +01:00
return - ENOMEM ;
our_env [ n_env + + ] = x ;
}
2013-12-18 17:41:16 +01:00
if ( home ) {
x = strappend ( " HOME= " , home ) ;
if ( ! x )
return - ENOMEM ;
our_env [ n_env + + ] = x ;
}
if ( username ) {
x = strappend ( " LOGNAME= " , username ) ;
if ( ! x )
return - ENOMEM ;
our_env [ n_env + + ] = x ;
x = strappend ( " USER= " , username ) ;
if ( ! x )
return - ENOMEM ;
our_env [ n_env + + ] = x ;
}
if ( shell ) {
x = strappend ( " SHELL= " , shell ) ;
if ( ! x )
return - ENOMEM ;
our_env [ n_env + + ] = x ;
}
if ( is_terminal_input ( c - > std_input ) | |
c - > std_output = = EXEC_OUTPUT_TTY | |
c - > std_error = = EXEC_OUTPUT_TTY | |
c - > tty_path ) {
x = strdup ( default_term_for_tty ( tty_path ( c ) ) ) ;
if ( ! x )
return - ENOMEM ;
our_env [ n_env + + ] = x ;
}
our_env [ n_env + + ] = NULL ;
2013-12-22 22:14:05 +01:00
assert ( n_env < = 10 ) ;
2013-12-18 17:41:16 +01:00
* ret = our_env ;
our_env = NULL ;
return 0 ;
}
2014-08-23 16:02:21 +02:00
static int exec_child ( ExecCommand * command ,
const ExecContext * context ,
const ExecParameters * params ,
ExecRuntime * runtime ,
char * * argv ,
int socket_fd ,
int * fds , unsigned n_fds ,
char * * files_env ,
int * error ) {
_cleanup_strv_free_ char * * our_env = NULL , * * pam_env = NULL , * * final_env = NULL , * * final_argv = NULL ;
2014-11-12 13:53:27 +01:00
_cleanup_free_ char * mac_selinux_context_net = NULL ;
2014-08-23 16:02:21 +02:00
const char * username = NULL , * home = NULL , * shell = NULL ;
unsigned n_dont_close = 0 ;
2014-08-22 19:02:03 +02:00
int dont_close [ n_fds + 4 ] ;
2014-11-28 20:51:01 +01:00
uid_t uid = UID_INVALID ;
gid_t gid = GID_INVALID ;
2014-08-23 16:02:21 +02:00
int i , err ;
2010-01-26 04:18:44 +01:00
2010-01-23 01:52:57 +01:00
assert ( command ) ;
assert ( context ) ;
2014-08-23 16:02:21 +02:00
assert ( params ) ;
assert ( error ) ;
rename_process_from_path ( command - > path ) ;
/* We reset exactly these signals, since they are the
* only ones we set to SIG_IGN in the main daemon . All
* others we leave untouched because we set them to
* SIG_DFL or a valid handler initially , both of which
* will be demoted to SIG_DFL . */
default_signals ( SIGNALS_CRASH_HANDLER ,
SIGNALS_IGNORE , - 1 ) ;
if ( context - > ignore_sigpipe )
ignore_signals ( SIGPIPE , - 1 ) ;
err = reset_signal_mask ( ) ;
if ( err < 0 ) {
* error = EXIT_SIGNAL_MASK ;
return err ;
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
if ( params - > idle_pipe )
do_idle_pipe_dance ( params - > idle_pipe ) ;
2010-04-15 06:19:54 +02:00
2014-08-23 16:02:21 +02:00
/* Close sockets very early to make sure we don't
* block init reexecution because it cannot bind its
* sockets */
log_forget_fds ( ) ;
2010-04-15 06:19:54 +02:00
2014-08-23 16:02:21 +02:00
if ( socket_fd > = 0 )
dont_close [ n_dont_close + + ] = socket_fd ;
if ( n_fds > 0 ) {
memcpy ( dont_close + n_dont_close , fds , sizeof ( int ) * n_fds ) ;
n_dont_close + = n_fds ;
}
2014-08-22 19:02:03 +02:00
if ( params - > bus_endpoint_fd > = 0 )
dont_close [ n_dont_close + + ] = params - > bus_endpoint_fd ;
2014-08-23 16:02:21 +02:00
if ( runtime ) {
if ( runtime - > netns_storage_socket [ 0 ] > = 0 )
dont_close [ n_dont_close + + ] = runtime - > netns_storage_socket [ 0 ] ;
if ( runtime - > netns_storage_socket [ 1 ] > = 0 )
dont_close [ n_dont_close + + ] = runtime - > netns_storage_socket [ 1 ] ;
2014-08-23 15:28:37 +02:00
}
2010-04-15 06:19:54 +02:00
2014-08-23 16:02:21 +02:00
err = close_all_fds ( dont_close , n_dont_close ) ;
if ( err < 0 ) {
* error = EXIT_FDS ;
return err ;
2011-03-04 03:44:43 +01:00
}
2014-08-23 16:02:21 +02:00
if ( ! context - > same_pgrp )
if ( setsid ( ) < 0 ) {
* error = EXIT_SETSID ;
return - errno ;
}
2010-04-15 03:11:11 +02:00
2014-08-23 16:02:21 +02:00
exec_context_tty_reset ( context ) ;
if ( params - > confirm_spawn ) {
char response ;
err = ask_for_confirmation ( & response , argv ) ;
if ( err = = - ETIMEDOUT )
write_confirm_message ( " Confirmation question timed out, assuming positive response. \n " ) ;
else if ( err < 0 )
write_confirm_message ( " Couldn't ask confirmation question, assuming positive response: %s \n " , strerror ( - err ) ) ;
else if ( response = = ' s ' ) {
write_confirm_message ( " Skipping execution. \n " ) ;
* error = EXIT_CONFIRM ;
return - ECANCELED ;
} else if ( response = = ' n ' ) {
write_confirm_message ( " Failing execution. \n " ) ;
* error = 0 ;
return 0 ;
}
}
2010-04-10 17:46:01 +02:00
2014-08-23 16:02:21 +02:00
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if ( socket_fd > = 0 )
fd_nonblock ( socket_fd , false ) ;
2010-01-27 04:31:52 +01:00
2014-08-23 16:02:21 +02:00
err = setup_input ( context , socket_fd , params - > apply_tty_stdin ) ;
if ( err < 0 ) {
* error = EXIT_STDIN ;
return err ;
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
err = setup_output ( context , STDOUT_FILENO , socket_fd , basename ( command - > path ) , params - > unit_id , params - > apply_tty_stdin ) ;
if ( err < 0 ) {
* error = EXIT_STDOUT ;
return err ;
}
err = setup_output ( context , STDERR_FILENO , socket_fd , basename ( command - > path ) , params - > unit_id , params - > apply_tty_stdin ) ;
if ( err < 0 ) {
* error = EXIT_STDERR ;
return err ;
}
if ( params - > cgroup_path ) {
2014-12-10 22:06:44 +01:00
err = cg_attach_everywhere ( params - > cgroup_supported , params - > cgroup_path , 0 , NULL , NULL ) ;
2014-08-26 21:11:35 +02:00
if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_CGROUP ;
return err ;
2010-01-27 06:17:51 +01:00
}
2014-08-23 16:02:21 +02:00
}
2010-01-27 06:17:51 +01:00
2014-08-23 16:02:21 +02:00
if ( context - > oom_score_adjust_set ) {
char t [ 16 ] ;
2012-04-24 14:28:00 +02:00
2014-08-23 16:02:21 +02:00
snprintf ( t , sizeof ( t ) , " %i " , context - > oom_score_adjust ) ;
char_array_0 ( t ) ;
2013-11-27 20:23:18 +01:00
2014-08-23 16:02:21 +02:00
if ( write_string_file ( " /proc/self/oom_score_adj " , t ) < 0 ) {
* error = EXIT_OOM_ADJUST ;
return - errno ;
2013-11-27 20:23:18 +01:00
}
2014-08-23 16:02:21 +02:00
}
if ( context - > nice_set )
if ( setpriority ( PRIO_PROCESS , 0 , context - > nice ) < 0 ) {
* error = EXIT_NICE ;
return - errno ;
2013-11-27 20:23:18 +01:00
}
2014-08-23 16:02:21 +02:00
if ( context - > cpu_sched_set ) {
struct sched_param param = {
. sched_priority = context - > cpu_sched_priority ,
} ;
err = sched_setscheduler ( 0 ,
context - > cpu_sched_policy |
( context - > cpu_sched_reset_on_fork ?
SCHED_RESET_ON_FORK : 0 ) ,
& param ) ;
2011-11-17 00:21:16 +01:00
if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_SETSCHEDULER ;
return - errno ;
2010-07-12 20:34:53 +02:00
}
2014-08-23 16:02:21 +02:00
}
2010-07-12 20:34:53 +02:00
2014-08-23 16:02:21 +02:00
if ( context - > cpuset )
if ( sched_setaffinity ( 0 , CPU_ALLOC_SIZE ( context - > cpuset_ncpus ) , context - > cpuset ) < 0 ) {
* error = EXIT_CPUAFFINITY ;
return - errno ;
2010-01-26 04:18:44 +01:00
}
2014-08-23 16:02:21 +02:00
if ( context - > ioprio_set )
if ( ioprio_set ( IOPRIO_WHO_PROCESS , 0 , context - > ioprio ) < 0 ) {
* error = EXIT_IOPRIO ;
return - errno ;
}
2010-08-30 23:31:27 +02:00
2014-08-23 16:02:21 +02:00
if ( context - > timer_slack_nsec ! = NSEC_INFINITY )
if ( prctl ( PR_SET_TIMERSLACK , context - > timer_slack_nsec ) < 0 ) {
* error = EXIT_TIMERSLACK ;
return - errno ;
2011-11-17 00:21:16 +01:00
}
2010-01-29 20:46:22 +01:00
2014-08-23 16:02:21 +02:00
if ( context - > personality ! = 0xffffffffUL )
if ( personality ( context - > personality ) < 0 ) {
* error = EXIT_PERSONALITY ;
return - errno ;
2011-11-17 00:21:16 +01:00
}
2010-01-30 01:55:42 +01:00
2014-08-23 16:02:21 +02:00
if ( context - > utmp_id )
utmp_put_init_process ( context - > utmp_id , getpid ( ) , getsid ( 0 ) , context - > tty_path ) ;
if ( context - > user ) {
username = context - > user ;
err = get_user_creds ( & username , & uid , & gid , & home , & shell ) ;
2011-11-17 00:21:16 +01:00
if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_USER ;
return err ;
2010-01-28 02:06:20 +01:00
}
2014-08-23 16:02:21 +02:00
if ( is_terminal_input ( context - > std_input ) ) {
err = chown_terminal ( STDIN_FILENO , uid ) ;
2011-11-17 00:21:16 +01:00
if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_STDIN ;
return err ;
2010-03-31 16:29:55 +02:00
}
2011-11-17 00:21:16 +01:00
}
2014-08-23 16:02:21 +02:00
}
2010-03-31 16:29:55 +02:00
2014-08-22 19:02:03 +02:00
# ifdef ENABLE_KDBUS
if ( params - > bus_endpoint_fd > = 0 & & context - > bus_endpoint ) {
2014-11-28 20:51:01 +01:00
uid_t ep_uid = ( uid = = UID_INVALID ) ? 0 : uid ;
2014-08-22 19:02:03 +02:00
err = bus_kernel_set_endpoint_policy ( params - > bus_endpoint_fd , ep_uid , context - > bus_endpoint ) ;
if ( err < 0 ) {
* error = EXIT_BUS_ENDPOINT ;
return err ;
}
}
# endif
2014-11-05 17:57:23 +01:00
/* If delegation is enabled we'll pass ownership of the cgroup
* ( but only in systemd ' s own controller hierarchy ! ) to the
* user of the new process . */
if ( params - > cgroup_path & & context - > user & & params - > cgroup_delegate ) {
2014-08-23 16:02:21 +02:00
err = cg_set_task_access ( SYSTEMD_CGROUP_CONTROLLER , params - > cgroup_path , 0644 , uid , gid ) ;
if ( err < 0 ) {
* error = EXIT_CGROUP ;
return err ;
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
err = cg_set_group_access ( SYSTEMD_CGROUP_CONTROLLER , params - > cgroup_path , 0755 , uid , gid ) ;
if ( err < 0 ) {
* error = EXIT_CGROUP ;
return err ;
2010-01-26 04:18:44 +01:00
}
2014-08-23 16:02:21 +02:00
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
if ( ! strv_isempty ( context - > runtime_directory ) & & params - > runtime_prefix ) {
char * * rt ;
2010-01-28 02:53:56 +01:00
2014-08-23 16:02:21 +02:00
STRV_FOREACH ( rt , context - > runtime_directory ) {
_cleanup_free_ char * p ;
2010-01-30 01:55:42 +01:00
2014-08-23 16:02:21 +02:00
p = strjoin ( params - > runtime_prefix , " / " , * rt , NULL ) ;
if ( ! p ) {
* error = EXIT_RUNTIME_DIRECTORY ;
return - ENOMEM ;
2010-01-30 01:55:42 +01:00
}
2014-08-23 16:02:21 +02:00
err = mkdir_safe ( p , context - > runtime_directory_mode , uid , gid ) ;
if ( err < 0 ) {
* error = EXIT_RUNTIME_DIRECTORY ;
return err ;
2010-01-30 01:55:42 +01:00
}
2014-08-23 16:02:21 +02:00
}
}
2010-01-30 01:55:42 +01:00
2014-08-23 16:02:21 +02:00
if ( params - > apply_permissions ) {
err = enforce_groups ( context , username , gid ) ;
if ( err < 0 ) {
* error = EXIT_GROUP ;
return err ;
}
}
2010-01-29 20:46:22 +01:00
2014-08-23 16:02:21 +02:00
umask ( context - > umask ) ;
2010-01-30 01:55:42 +01:00
2014-08-23 16:02:21 +02:00
# ifdef HAVE_PAM
if ( params - > apply_permissions & & context - > pam_name & & username ) {
err = setup_pam ( context - > pam_name , username , uid , context - > tty_path , & pam_env , fds , n_fds ) ;
if ( err < 0 ) {
* error = EXIT_PAM ;
return err ;
}
}
# endif
2014-02-19 02:15:24 +01:00
2014-08-23 16:02:21 +02:00
if ( context - > private_network & & runtime & & runtime - > netns_storage_socket [ 0 ] > = 0 ) {
err = setup_netns ( runtime - > netns_storage_socket ) ;
if ( err < 0 ) {
* error = EXIT_NETWORK ;
return err ;
}
}
2010-10-08 16:06:23 +02:00
2014-08-23 16:02:21 +02:00
if ( ! strv_isempty ( context - > read_write_dirs ) | |
! strv_isempty ( context - > read_only_dirs ) | |
! strv_isempty ( context - > inaccessible_dirs ) | |
context - > mount_flags ! = 0 | |
( context - > private_tmp & & runtime & & ( runtime - > tmp_dir | | runtime - > var_tmp_dir ) ) | |
2014-08-22 19:02:03 +02:00
params - > bus_endpoint_path | |
2014-08-23 16:02:21 +02:00
context - > private_devices | |
context - > protect_system ! = PROTECT_SYSTEM_NO | |
context - > protect_home ! = PROTECT_HOME_NO ) {
char * tmp = NULL , * var = NULL ;
/* The runtime struct only contains the parent
* of the private / tmp , which is
* non - accessible to world users . Inside of it
* there ' s a / tmp that is sticky , and that ' s
* the one we want to use here . */
if ( context - > private_tmp & & runtime ) {
if ( runtime - > tmp_dir )
tmp = strappenda ( runtime - > tmp_dir , " /tmp " ) ;
if ( runtime - > var_tmp_dir )
var = strappenda ( runtime - > var_tmp_dir , " /tmp " ) ;
}
2010-04-13 21:13:49 +02:00
2014-08-23 16:02:21 +02:00
err = setup_namespace (
context - > read_write_dirs ,
context - > read_only_dirs ,
context - > inaccessible_dirs ,
tmp ,
var ,
2014-08-22 19:02:03 +02:00
params - > bus_endpoint_path ,
2014-08-23 16:02:21 +02:00
context - > private_devices ,
context - > protect_home ,
context - > protect_system ,
context - > mount_flags ) ;
2014-10-17 11:51:46 +02:00
if ( err = = - EPERM )
2014-11-28 13:17:02 +01:00
log_unit_warning_errno ( params - > unit_id , err , " Failed to set up file system namespace due to lack of privileges. Execution sandbox will not be in effect: %m " ) ;
2014-10-17 11:51:46 +02:00
else if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_NAMESPACE ;
return err ;
2010-02-14 22:43:08 +01:00
}
2014-08-23 16:02:21 +02:00
}
2010-02-14 22:43:08 +01:00
2014-08-23 16:02:21 +02:00
if ( params - > apply_chroot ) {
if ( context - > root_directory )
if ( chroot ( context - > root_directory ) < 0 ) {
* error = EXIT_CHROOT ;
return - errno ;
2013-07-11 01:56:12 +02:00
}
2014-08-23 16:02:21 +02:00
if ( chdir ( context - > working_directory ? context - > working_directory : " / " ) < 0 ) {
* error = EXIT_CHDIR ;
return - errno ;
}
} else {
_cleanup_free_ char * d = NULL ;
2013-07-11 01:56:12 +02:00
2014-08-23 16:02:21 +02:00
if ( asprintf ( & d , " %s/%s " ,
context - > root_directory ? context - > root_directory : " " ,
context - > working_directory ? context - > working_directory : " " ) < 0 ) {
* error = EXIT_MEMORY ;
return - ENOMEM ;
2013-07-11 01:56:12 +02:00
}
2014-08-23 16:02:21 +02:00
if ( chdir ( d ) < 0 ) {
* error = EXIT_CHDIR ;
return - errno ;
}
}
2014-03-03 17:14:07 +01:00
2014-11-12 13:53:27 +01:00
# ifdef HAVE_SELINUX
if ( params - > apply_permissions & & mac_selinux_use ( ) & & params - > selinux_context_net & & socket_fd > = 0 ) {
err = mac_selinux_get_child_mls_label ( socket_fd , command - > path , context - > selinux_context , & mac_selinux_context_net ) ;
if ( err < 0 ) {
* error = EXIT_SELINUX_CONTEXT ;
return err ;
}
}
# endif
2014-08-23 16:02:21 +02:00
/* We repeat the fd closing here, to make sure that
* nothing is leaked from the PAM modules . Note that
* we are more aggressive this time since socket_fd
2014-08-22 19:02:03 +02:00
* and the netns fds we don ' t need anymore . The custom
* endpoint fd was needed to upload the policy and can
* now be closed as well . */
2014-08-23 16:02:21 +02:00
err = close_all_fds ( fds , n_fds ) ;
if ( err > = 0 )
err = shift_fds ( fds , n_fds ) ;
if ( err > = 0 )
err = flags_fds ( fds , n_fds , context - > non_blocking ) ;
if ( err < 0 ) {
* error = EXIT_FDS ;
return err ;
}
2014-03-03 17:14:07 +01:00
2014-08-23 16:02:21 +02:00
if ( params - > apply_permissions ) {
2014-03-03 17:14:07 +01:00
2014-08-23 16:02:21 +02:00
for ( i = 0 ; i < _RLIMIT_MAX ; i + + ) {
if ( ! context - > rlimit [ i ] )
continue ;
if ( setrlimit_closest ( i , context - > rlimit [ i ] ) < 0 ) {
* error = EXIT_LIMITS ;
return - errno ;
2014-03-03 17:14:07 +01:00
}
}
2014-08-23 16:02:21 +02:00
if ( context - > capability_bounding_set_drop ) {
err = capability_bounding_set_drop ( context - > capability_bounding_set_drop , false ) ;
2011-11-17 00:21:16 +01:00
if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_CAPABILITIES ;
return err ;
2011-06-30 02:15:01 +02:00
}
2011-11-17 00:21:16 +01:00
}
2011-06-30 02:15:01 +02:00
2014-11-24 12:46:20 +01:00
# ifdef HAVE_SMACK
if ( context - > smack_process_label ) {
err = mac_smack_apply_pid ( 0 , context - > smack_process_label ) ;
if ( err < 0 ) {
* error = EXIT_SMACK_PROCESS_LABEL ;
return err ;
}
}
# endif
2014-08-23 16:02:21 +02:00
if ( context - > user ) {
err = enforce_user ( context , uid ) ;
2011-11-17 00:21:16 +01:00
if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_USER ;
return err ;
2010-06-16 21:54:17 +02:00
}
}
2014-08-23 16:02:21 +02:00
/* PR_GET_SECUREBITS is not privileged, while
* PR_SET_SECUREBITS is . So to suppress
* potential EPERMs we ' ll try not to call
* PR_SET_SECUREBITS unless necessary . */
if ( prctl ( PR_GET_SECUREBITS ) ! = context - > secure_bits )
if ( prctl ( PR_SET_SECUREBITS , context - > secure_bits ) < 0 ) {
* error = EXIT_SECUREBITS ;
return - errno ;
2011-08-02 05:24:58 +02:00
}
2010-06-16 21:54:17 +02:00
2014-08-23 16:02:21 +02:00
if ( context - > capabilities )
if ( cap_set_proc ( context - > capabilities ) < 0 ) {
* error = EXIT_CAPABILITIES ;
return - errno ;
2013-11-27 20:23:18 +01:00
}
2014-08-23 16:02:21 +02:00
if ( context - > no_new_privileges )
if ( prctl ( PR_SET_NO_NEW_PRIVS , 1 , 0 , 0 , 0 ) < 0 ) {
* error = EXIT_NO_NEW_PRIVILEGES ;
return - errno ;
}
# ifdef HAVE_SECCOMP
if ( context - > address_families_whitelist | |
! set_isempty ( context - > address_families ) ) {
err = apply_address_families ( context ) ;
2011-11-17 00:21:16 +01:00
if ( err < 0 ) {
2014-08-23 16:02:21 +02:00
* error = EXIT_ADDRESS_FAMILIES ;
return err ;
2011-11-17 00:21:16 +01:00
}
}
2010-06-16 16:39:28 +02:00
2014-08-23 16:02:21 +02:00
if ( context - > syscall_whitelist | |
! set_isempty ( context - > syscall_filter ) | |
! set_isempty ( context - > syscall_archs ) ) {
err = apply_seccomp ( context ) ;
if ( err < 0 ) {
* error = EXIT_SECCOMP ;
return err ;
2010-02-14 22:43:08 +01:00
}
2014-08-23 16:02:21 +02:00
}
# endif
2010-02-14 22:43:08 +01:00
2014-08-23 16:02:21 +02:00
# ifdef HAVE_SELINUX
2014-10-23 17:34:30 +02:00
if ( mac_selinux_use ( ) ) {
2014-11-12 13:53:27 +01:00
char * exec_context = mac_selinux_context_net ? : context - > selinux_context ;
2014-07-24 10:40:28 +02:00
2014-11-12 13:53:27 +01:00
if ( exec_context ) {
err = setexeccon ( exec_context ) ;
2014-07-24 10:40:28 +02:00
if ( err < 0 ) {
* error = EXIT_SELINUX_CONTEXT ;
return err ;
}
2010-02-14 22:43:08 +01:00
}
}
2014-08-23 16:02:21 +02:00
# endif
2010-02-14 22:43:08 +01:00
2014-08-23 16:02:21 +02:00
# ifdef HAVE_APPARMOR
2014-10-23 17:34:30 +02:00
if ( context - > apparmor_profile & & mac_apparmor_use ( ) ) {
2014-08-23 16:02:21 +02:00
err = aa_change_onexec ( context - > apparmor_profile ) ;
if ( err < 0 & & ! context - > apparmor_profile_ignore ) {
* error = EXIT_APPARMOR_PROFILE ;
2014-10-11 10:13:43 +02:00
return - errno ;
2014-08-23 16:02:21 +02:00
}
2010-01-26 04:18:44 +01:00
}
2014-08-23 16:02:21 +02:00
# endif
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
err = build_environment ( context , n_fds , params - > watchdog_usec , home , username , shell , & our_env ) ;
if ( err < 0 ) {
* error = EXIT_MEMORY ;
return err ;
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
final_env = strv_env_merge ( 5 ,
params - > environment ,
our_env ,
context - > environment ,
files_env ,
pam_env ,
NULL ) ;
if ( ! final_env ) {
* error = EXIT_MEMORY ;
return - ENOMEM ;
}
2010-02-14 22:43:08 +01:00
2014-08-23 16:02:21 +02:00
final_argv = replace_env_argv ( argv , final_env ) ;
if ( ! final_argv ) {
* error = EXIT_MEMORY ;
return - ENOMEM ;
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
final_env = strv_env_clean ( final_env ) ;
2011-03-18 03:13:15 +01:00
2014-08-23 16:02:21 +02:00
if ( _unlikely_ ( log_get_max_level ( ) > = LOG_PRI ( LOG_DEBUG ) ) ) {
_cleanup_free_ char * line ;
2010-02-14 22:43:08 +01:00
2014-08-23 16:02:21 +02:00
line = exec_command_line ( final_argv ) ;
if ( line ) {
log_open ( ) ;
2014-11-28 02:05:14 +01:00
log_unit_struct ( params - > unit_id ,
LOG_DEBUG ,
2014-08-23 16:02:21 +02:00
" EXECUTABLE=%s " , command - > path ,
2014-11-28 02:05:14 +01:00
LOG_MESSAGE ( " Executing: %s " , line ) ,
2014-08-23 16:02:21 +02:00
NULL ) ;
log_close ( ) ;
}
}
execve ( command - > path , final_argv , final_env ) ;
* error = EXIT_EXEC ;
return - errno ;
}
2010-02-14 22:43:08 +01:00
2014-08-23 16:02:21 +02:00
int exec_spawn ( ExecCommand * command ,
const ExecContext * context ,
const ExecParameters * params ,
ExecRuntime * runtime ,
pid_t * ret ) {
2012-07-17 04:17:53 +02:00
2014-08-23 16:02:21 +02:00
_cleanup_strv_free_ char * * files_env = NULL ;
int * fds = NULL ; unsigned n_fds = 0 ;
char * line , * * argv ;
int socket_fd ;
pid_t pid ;
int err ;
2012-07-17 04:17:53 +02:00
2014-08-23 16:02:21 +02:00
assert ( command ) ;
assert ( context ) ;
assert ( ret ) ;
assert ( params ) ;
assert ( params - > fds | | params - > n_fds < = 0 ) ;
2014-02-25 20:37:03 +01:00
2014-08-23 16:02:21 +02:00
if ( context - > std_input = = EXEC_INPUT_SOCKET | |
context - > std_output = = EXEC_OUTPUT_SOCKET | |
context - > std_error = = EXEC_OUTPUT_SOCKET ) {
2014-02-12 18:28:21 +01:00
2014-08-23 16:02:21 +02:00
if ( params - > n_fds ! = 1 )
return - EINVAL ;
2014-02-20 16:19:44 +01:00
2014-08-23 16:02:21 +02:00
socket_fd = params - > fds [ 0 ] ;
} else {
socket_fd = - 1 ;
fds = params - > fds ;
n_fds = params - > n_fds ;
}
2010-01-30 01:55:42 +01:00
2014-10-17 11:46:01 +02:00
err = exec_context_load_environment ( context , params - > unit_id , & files_env ) ;
2014-08-23 16:02:21 +02:00
if ( err < 0 ) {
2014-11-28 02:05:14 +01:00
log_unit_struct ( params - > unit_id ,
LOG_ERR ,
LOG_MESSAGE ( " Failed to load environment files: %s " , strerror ( - err ) ) ,
LOG_ERRNO ( - err ) ,
NULL ) ;
2014-08-23 16:02:21 +02:00
return err ;
}
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
argv = params - > argv ? : command - > argv ;
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
line = exec_command_line ( argv ) ;
if ( ! line )
return log_oom ( ) ;
2010-07-08 04:09:59 +02:00
2014-11-28 02:05:14 +01:00
log_unit_struct ( params - > unit_id ,
LOG_DEBUG ,
2014-08-23 16:02:21 +02:00
" EXECUTABLE=%s " , command - > path ,
2014-11-28 02:05:14 +01:00
LOG_MESSAGE ( " About to execute: %s " , line ) ,
2014-08-23 16:02:21 +02:00
NULL ) ;
free ( line ) ;
pid = fork ( ) ;
if ( pid < 0 )
return - errno ;
if ( pid = = 0 ) {
int r ;
2010-01-26 04:18:44 +01:00
2014-08-23 16:02:21 +02:00
err = exec_child ( command ,
context ,
params ,
runtime ,
argv ,
socket_fd ,
fds , n_fds ,
files_env ,
& r ) ;
2011-11-17 00:21:16 +01:00
if ( r ! = 0 ) {
log_open ( ) ;
2014-11-28 02:05:14 +01:00
log_struct ( LOG_ERR ,
LOG_MESSAGE_ID ( SD_MESSAGE_SPAWN_FAILED ) ,
2012-10-11 00:11:24 +02:00
" EXECUTABLE=%s " , command - > path ,
2014-11-28 02:05:14 +01:00
LOG_MESSAGE ( " Failed at step %s spawning %s: %s " ,
exit_status_to_string ( r , EXIT_STATUS_SYSTEMD ) ,
command - > path , strerror ( - err ) ) ,
LOG_ERRNO ( - err ) ,
2012-10-11 00:11:24 +02:00
NULL ) ;
log_close ( ) ;
2011-11-17 00:21:16 +01:00
}
2010-01-26 04:18:44 +01:00
_exit ( r ) ;
}
2014-11-28 02:05:14 +01:00
log_unit_struct ( params - > unit_id ,
LOG_DEBUG ,
LOG_MESSAGE ( " Forked %s as " PID_FMT ,
command - > path , pid ) ,
2013-03-25 00:45:16 +01:00
NULL ) ;
2012-10-11 00:11:24 +02:00
2010-04-13 02:06:27 +02:00
/* We add the new process to the cgroup both in the child (so
* that we can be sure that no user code is ever executed
* outside of the cgroup ) and in the parent ( so that we can be
* sure that when we kill the cgroup the process will be
* killed too ) . */
2014-08-23 16:02:21 +02:00
if ( params - > cgroup_path )
cg_attach ( SYSTEMD_CGROUP_CONTROLLER , params - > cgroup_path , pid ) ;
2010-01-27 05:30:58 +01:00
2010-07-04 18:49:58 +02:00
exec_status_start ( & command - > exec_status , pid ) ;
2010-04-10 05:03:14 +02:00
2010-01-26 04:18:44 +01:00
* ret = pid ;
2010-01-23 01:52:57 +01:00
return 0 ;
}
2010-01-26 04:18:44 +01:00
void exec_context_init ( ExecContext * c ) {
assert ( c ) ;
2011-08-01 20:52:18 +02:00
c - > umask = 0022 ;
2010-01-29 20:46:22 +01:00
c - > ioprio = IOPRIO_PRIO_VALUE ( IOPRIO_CLASS_BE , 0 ) ;
2010-01-30 01:55:42 +01:00
c - > cpu_sched_policy = SCHED_OTHER ;
2010-01-28 02:06:20 +01:00
c - > syslog_priority = LOG_DAEMON | LOG_INFO ;
2010-07-05 01:08:13 +02:00
c - > syslog_level_prefix = true ;
2012-02-09 03:18:04 +01:00
c - > ignore_sigpipe = true ;
2014-07-29 12:23:31 +02:00
c - > timer_slack_nsec = NSEC_INFINITY ;
2014-02-19 02:15:24 +01:00
c - > personality = 0xffffffffUL ;
2014-03-03 17:14:07 +01:00
c - > runtime_directory_mode = 0755 ;
2010-01-26 04:18:44 +01:00
}
2013-11-27 20:23:18 +01:00
void exec_context_done ( ExecContext * c ) {
2010-01-23 01:52:57 +01:00
unsigned l ;
assert ( c ) ;
strv_free ( c - > environment ) ;
2010-01-26 04:18:44 +01:00
c - > environment = NULL ;
2010-01-23 01:52:57 +01:00
2011-03-04 03:44:43 +01:00
strv_free ( c - > environment_files ) ;
c - > environment_files = NULL ;
2010-01-26 04:18:44 +01:00
for ( l = 0 ; l < ELEMENTSOF ( c - > rlimit ) ; l + + ) {
2010-01-23 01:52:57 +01:00
free ( c - > rlimit [ l ] ) ;
2010-01-26 04:18:44 +01:00
c - > rlimit [ l ] = NULL ;
}
2010-01-29 20:46:22 +01:00
free ( c - > working_directory ) ;
c - > working_directory = NULL ;
free ( c - > root_directory ) ;
c - > root_directory = NULL ;
2010-01-23 01:52:57 +01:00
2010-04-13 02:06:27 +02:00
free ( c - > tty_path ) ;
c - > tty_path = NULL ;
2010-01-28 02:06:20 +01:00
free ( c - > syslog_identifier ) ;
c - > syslog_identifier = NULL ;
2010-01-23 01:52:57 +01:00
free ( c - > user ) ;
2010-01-26 04:18:44 +01:00
c - > user = NULL ;
2010-01-23 01:52:57 +01:00
free ( c - > group ) ;
2010-01-26 04:18:44 +01:00
c - > group = NULL ;
strv_free ( c - > supplementary_groups ) ;
c - > supplementary_groups = NULL ;
2010-01-30 01:55:42 +01:00
2010-06-16 21:54:17 +02:00
free ( c - > pam_name ) ;
c - > pam_name = NULL ;
2010-01-30 01:55:42 +01:00
if ( c - > capabilities ) {
cap_free ( c - > capabilities ) ;
c - > capabilities = NULL ;
}
2010-04-21 22:15:06 +02:00
strv_free ( c - > read_only_dirs ) ;
c - > read_only_dirs = NULL ;
strv_free ( c - > read_write_dirs ) ;
c - > read_write_dirs = NULL ;
strv_free ( c - > inaccessible_dirs ) ;
c - > inaccessible_dirs = NULL ;
2010-07-04 16:44:58 +02:00
if ( c - > cpuset )
CPU_FREE ( c - > cpuset ) ;
2011-01-06 23:52:17 +01:00
free ( c - > utmp_id ) ;
c - > utmp_id = NULL ;
2012-07-17 15:55:23 +02:00
2014-02-06 10:05:16 +01:00
free ( c - > selinux_context ) ;
c - > selinux_context = NULL ;
2014-02-20 16:19:44 +01:00
free ( c - > apparmor_profile ) ;
c - > apparmor_profile = NULL ;
2014-02-12 18:28:21 +01:00
set_free ( c - > syscall_filter ) ;
c - > syscall_filter = NULL ;
2014-02-13 00:24:00 +01:00
set_free ( c - > syscall_archs ) ;
c - > syscall_archs = NULL ;
2014-02-25 20:37:03 +01:00
set_free ( c - > address_families ) ;
c - > address_families = NULL ;
2014-03-03 17:14:07 +01:00
strv_free ( c - > runtime_directory ) ;
c - > runtime_directory = NULL ;
2014-08-18 19:55:32 +02:00
bus_endpoint_free ( c - > bus_endpoint ) ;
c - > bus_endpoint = NULL ;
2014-03-03 17:14:07 +01:00
}
int exec_context_destroy_runtime_directory ( ExecContext * c , const char * runtime_prefix ) {
char * * i ;
assert ( c ) ;
if ( ! runtime_prefix )
return 0 ;
STRV_FOREACH ( i , c - > runtime_directory ) {
_cleanup_free_ char * p ;
p = strjoin ( runtime_prefix , " / " , * i , NULL ) ;
if ( ! p )
return - ENOMEM ;
/* We execute this synchronously, since we need to be
* sure this is gone when we start the service
* next . */
rm_rf_dangerous ( p , false , true , false ) ;
}
return 0 ;
2010-01-23 01:52:57 +01:00
}
2010-04-10 17:47:07 +02:00
void exec_command_done ( ExecCommand * c ) {
assert ( c ) ;
free ( c - > path ) ;
c - > path = NULL ;
strv_free ( c - > argv ) ;
c - > argv = NULL ;
}
void exec_command_done_array ( ExecCommand * c , unsigned n ) {
unsigned i ;
for ( i = 0 ; i < n ; i + + )
exec_command_done ( c + i ) ;
}
2010-01-23 01:52:57 +01:00
void exec_command_free_list ( ExecCommand * c ) {
ExecCommand * i ;
while ( ( i = c ) ) {
2013-10-14 06:10:14 +02:00
LIST_REMOVE ( command , c , i ) ;
2010-04-10 17:47:07 +02:00
exec_command_done ( i ) ;
2010-01-23 01:52:57 +01:00
free ( i ) ;
}
}
2010-01-26 04:18:44 +01:00
void exec_command_free_array ( ExecCommand * * c , unsigned n ) {
unsigned i ;
for ( i = 0 ; i < n ; i + + ) {
exec_command_free_list ( c [ i ] ) ;
c [ i ] = NULL ;
}
}
2014-10-17 11:46:01 +02:00
int exec_context_load_environment ( const ExecContext * c , const char * unit_id , char * * * l ) {
2011-03-04 03:44:43 +01:00
char * * i , * * r = NULL ;
assert ( c ) ;
assert ( l ) ;
STRV_FOREACH ( i , c - > environment_files ) {
char * fn ;
int k ;
bool ignore = false ;
char * * p ;
2013-04-18 09:11:22 +02:00
_cleanup_globfree_ glob_t pglob = { } ;
2013-01-02 12:41:52 +01:00
int count , n ;
2011-03-04 03:44:43 +01:00
fn = * i ;
if ( fn [ 0 ] = = ' - ' ) {
ignore = true ;
fn + + ;
}
if ( ! path_is_absolute ( fn ) ) {
if ( ignore )
continue ;
strv_free ( r ) ;
return - EINVAL ;
}
2013-01-02 12:41:52 +01:00
/* Filename supports globbing, take all matching files */
errno = 0 ;
if ( glob ( fn , 0 , NULL , & pglob ) ! = 0 ) {
if ( ignore )
continue ;
2011-03-04 03:44:43 +01:00
2013-01-02 12:41:52 +01:00
strv_free ( r ) ;
return errno ? - errno : - EINVAL ;
}
count = pglob . gl_pathc ;
if ( count = = 0 ) {
2011-03-04 03:44:43 +01:00
if ( ignore )
continue ;
strv_free ( r ) ;
2013-01-02 12:41:52 +01:00
return - EINVAL ;
2011-03-04 03:44:43 +01:00
}
2013-01-02 12:41:52 +01:00
for ( n = 0 ; n < count ; n + + ) {
2014-07-03 17:50:55 +02:00
k = load_env_file ( NULL , pglob . gl_pathv [ n ] , NULL , & p ) ;
2013-01-02 12:41:52 +01:00
if ( k < 0 ) {
if ( ignore )
continue ;
2011-03-04 03:44:43 +01:00
2013-01-02 12:41:52 +01:00
strv_free ( r ) ;
return k ;
2013-07-02 13:24:48 +02:00
}
2013-04-17 15:25:02 +02:00
/* Log invalid environment variables with filename */
2013-07-02 13:24:48 +02:00
if ( p )
2014-10-17 11:46:01 +02:00
p = strv_env_clean_log ( p , unit_id , pglob . gl_pathv [ n ] ) ;
2011-03-04 03:44:43 +01:00
2013-01-02 12:41:52 +01:00
if ( r = = NULL )
r = p ;
else {
char * * m ;
2011-03-04 03:44:43 +01:00
2013-01-02 12:41:52 +01:00
m = strv_env_merge ( 2 , r , p ) ;
strv_free ( r ) ;
strv_free ( p ) ;
2013-03-25 00:09:19 +01:00
if ( ! m )
2013-01-02 12:41:52 +01:00
return - ENOMEM ;
r = m ;
}
2011-03-04 03:44:43 +01:00
}
}
* l = r ;
return 0 ;
}
2013-02-28 01:36:55 +01:00
static bool tty_may_match_dev_console ( const char * tty ) {
2014-06-24 19:00:32 +02:00
_cleanup_free_ char * active = NULL ;
char * console ;
2013-02-28 01:36:55 +01:00
if ( startswith ( tty , " /dev/ " ) )
tty + = 5 ;
/* trivial identity? */
if ( streq ( tty , " console " ) )
return true ;
console = resolve_dev_console ( & active ) ;
/* if we could not resolve, assume it may */
if ( ! console )
return true ;
/* "tty0" means the active VC, so it may be the same sometimes */
2014-06-24 19:00:32 +02:00
return streq ( console , tty ) | | ( streq ( console , " tty0 " ) & & tty_is_vc ( tty ) ) ;
2013-02-28 01:36:55 +01:00
}
bool exec_context_may_touch_console ( ExecContext * ec ) {
return ( ec - > tty_reset | | ec - > tty_vhangup | | ec - > tty_vt_disallocate | |
is_terminal_input ( ec - > std_input ) | |
is_terminal_output ( ec - > std_output ) | |
is_terminal_output ( ec - > std_error ) ) & &
tty_may_match_dev_console ( tty_path ( ec ) ) ;
}
2010-04-21 22:15:06 +02:00
static void strv_fprintf ( FILE * f , char * * l ) {
char * * g ;
assert ( f ) ;
STRV_FOREACH ( g , l )
fprintf ( f , " %s " , * g ) ;
}
2010-01-23 01:52:57 +01:00
void exec_context_dump ( ExecContext * c , FILE * f , const char * prefix ) {
2013-06-06 00:44:16 +02:00
char * * e ;
2010-01-30 01:55:42 +01:00
unsigned i ;
2010-01-29 20:46:22 +01:00
2010-01-23 01:52:57 +01:00
assert ( c ) ;
assert ( f ) ;
2013-06-27 04:14:27 +02:00
prefix = strempty ( prefix ) ;
2010-01-23 01:52:57 +01:00
fprintf ( f ,
2010-01-30 01:55:42 +01:00
" %sUMask: %04o \n "
" %sWorkingDirectory: %s \n "
2010-02-12 02:00:18 +01:00
" %sRootDirectory: %s \n "
2010-04-21 22:15:06 +02:00
" %sNonBlocking: %s \n "
2011-06-30 00:11:25 +02:00
" %sPrivateTmp: %s \n "
2012-07-19 23:47:10 +02:00
" %sPrivateNetwork: %s \n "
2014-01-20 19:54:51 +01:00
" %sPrivateDevices: %s \n "
2014-06-04 18:07:55 +02:00
" %sProtectHome: %s \n "
" %sProtectSystem: %s \n "
2014-08-19 19:16:08 +02:00
" %sIgnoreSIGPIPE: %s \n " ,
2010-01-23 01:52:57 +01:00
prefix , c - > umask ,
2010-01-29 20:46:22 +01:00
prefix , c - > working_directory ? c - > working_directory : " / " ,
2010-02-12 02:00:18 +01:00
prefix , c - > root_directory ? c - > root_directory : " / " ,
2010-04-21 22:15:06 +02:00
prefix , yes_no ( c - > non_blocking ) ,
2011-06-30 00:11:25 +02:00
prefix , yes_no ( c - > private_tmp ) ,
2012-07-19 23:47:10 +02:00
prefix , yes_no ( c - > private_network ) ,
2014-01-20 19:54:51 +01:00
prefix , yes_no ( c - > private_devices ) ,
2014-06-04 18:07:55 +02:00
prefix , protect_home_to_string ( c - > protect_home ) ,
prefix , protect_system_to_string ( c - > protect_system ) ,
2014-08-19 19:16:08 +02:00
prefix , yes_no ( c - > ignore_sigpipe ) ) ;
2010-01-28 02:53:56 +01:00
2011-03-04 03:44:43 +01:00
STRV_FOREACH ( e , c - > environment )
fprintf ( f , " %sEnvironment: %s \n " , prefix , * e ) ;
STRV_FOREACH ( e , c - > environment_files )
fprintf ( f , " %sEnvironmentFile: %s \n " , prefix , * e ) ;
2010-01-30 01:55:42 +01:00
2010-01-28 02:53:56 +01:00
if ( c - > nice_set )
fprintf ( f ,
" %sNice: %i \n " ,
prefix , c - > nice ) ;
2010-08-31 01:33:39 +02:00
if ( c - > oom_score_adjust_set )
2010-01-28 02:53:56 +01:00
fprintf ( f ,
2010-08-31 01:33:39 +02:00
" %sOOMScoreAdjust: %i \n " ,
prefix , c - > oom_score_adjust ) ;
2010-01-29 20:46:22 +01:00
2010-01-30 01:55:42 +01:00
for ( i = 0 ; i < RLIM_NLIMITS ; i + + )
if ( c - > rlimit [ i ] )
2014-04-25 13:45:15 +02:00
fprintf ( f , " %s%s: " RLIM_FMT " \n " ,
prefix , rlimit_to_string ( i ) , c - > rlimit [ i ] - > rlim_max ) ;
2010-01-30 01:55:42 +01:00
2012-10-30 14:29:38 +01:00
if ( c - > ioprio_set ) {
2014-02-19 17:49:00 +01:00
_cleanup_free_ char * class_str = NULL ;
2012-10-30 14:29:38 +01:00
2014-02-19 17:49:00 +01:00
ioprio_class_to_string_alloc ( IOPRIO_PRIO_CLASS ( c - > ioprio ) , & class_str ) ;
2010-01-29 20:46:22 +01:00
fprintf ( f ,
" %sIOSchedulingClass: %s \n "
" %sIOPriority: %i \n " ,
2012-10-30 14:29:38 +01:00
prefix , strna ( class_str ) ,
2010-01-29 20:46:22 +01:00
prefix , ( int ) IOPRIO_PRIO_DATA ( c - > ioprio ) ) ;
2012-10-30 14:29:38 +01:00
}
2010-01-30 01:55:42 +01:00
2012-10-30 14:29:38 +01:00
if ( c - > cpu_sched_set ) {
2014-02-19 17:49:00 +01:00
_cleanup_free_ char * policy_str = NULL ;
2012-10-30 14:29:38 +01:00
2014-02-19 17:49:00 +01:00
sched_policy_to_string_alloc ( c - > cpu_sched_policy , & policy_str ) ;
2010-01-30 01:55:42 +01:00
fprintf ( f ,
" %sCPUSchedulingPolicy: %s \n "
2010-02-02 12:50:04 +01:00
" %sCPUSchedulingPriority: %i \n "
" %sCPUSchedulingResetOnFork: %s \n " ,
2012-10-30 14:29:38 +01:00
prefix , strna ( policy_str ) ,
2010-02-02 12:50:04 +01:00
prefix , c - > cpu_sched_priority ,
prefix , yes_no ( c - > cpu_sched_reset_on_fork ) ) ;
2013-01-09 21:03:11 +01:00
}
2010-01-30 01:55:42 +01:00
2010-07-04 16:44:58 +02:00
if ( c - > cpuset ) {
2010-01-30 01:55:42 +01:00
fprintf ( f , " %sCPUAffinity: " , prefix ) ;
2010-07-04 16:44:58 +02:00
for ( i = 0 ; i < c - > cpuset_ncpus ; i + + )
if ( CPU_ISSET_S ( i , CPU_ALLOC_SIZE ( c - > cpuset_ncpus ) , c - > cpuset ) )
2013-12-25 19:00:12 +01:00
fprintf ( f , " %u " , i ) ;
2010-01-30 01:55:42 +01:00
fputs ( " \n " , f ) ;
}
2014-07-29 12:23:31 +02:00
if ( c - > timer_slack_nsec ! = NSEC_INFINITY )
2013-12-30 23:22:26 +01:00
fprintf ( f , " %sTimerSlackNSec: " NSEC_FMT " \n " , prefix , c - > timer_slack_nsec ) ;
2010-01-30 01:55:42 +01:00
fprintf ( f ,
2010-04-13 02:06:27 +02:00
" %sStandardInput: %s \n "
" %sStandardOutput: %s \n "
" %sStandardError: %s \n " ,
prefix , exec_input_to_string ( c - > std_input ) ,
prefix , exec_output_to_string ( c - > std_output ) ,
prefix , exec_output_to_string ( c - > std_error ) ) ;
if ( c - > tty_path )
fprintf ( f ,
2011-05-18 01:07:31 +02:00
" %sTTYPath: %s \n "
" %sTTYReset: %s \n "
" %sTTYVHangup: %s \n "
" %sTTYVTDisallocate: %s \n " ,
prefix , c - > tty_path ,
prefix , yes_no ( c - > tty_reset ) ,
prefix , yes_no ( c - > tty_vhangup ) ,
prefix , yes_no ( c - > tty_vt_disallocate ) ) ;
2010-01-30 01:55:42 +01:00
2014-01-01 04:35:54 +01:00
if ( c - > std_output = = EXEC_OUTPUT_SYSLOG | |
c - > std_output = = EXEC_OUTPUT_KMSG | |
c - > std_output = = EXEC_OUTPUT_JOURNAL | |
c - > std_output = = EXEC_OUTPUT_SYSLOG_AND_CONSOLE | |
c - > std_output = = EXEC_OUTPUT_KMSG_AND_CONSOLE | |
c - > std_output = = EXEC_OUTPUT_JOURNAL_AND_CONSOLE | |
c - > std_error = = EXEC_OUTPUT_SYSLOG | |
c - > std_error = = EXEC_OUTPUT_KMSG | |
c - > std_error = = EXEC_OUTPUT_JOURNAL | |
c - > std_error = = EXEC_OUTPUT_SYSLOG_AND_CONSOLE | |
c - > std_error = = EXEC_OUTPUT_KMSG_AND_CONSOLE | |
c - > std_error = = EXEC_OUTPUT_JOURNAL_AND_CONSOLE ) {
2012-10-30 14:29:38 +01:00
2014-01-01 04:35:54 +01:00
_cleanup_free_ char * fac_str = NULL , * lvl_str = NULL ;
2012-10-30 14:29:38 +01:00
2014-01-01 04:35:54 +01:00
log_facility_unshifted_to_string_alloc ( c - > syslog_priority > > 3 , & fac_str ) ;
log_level_to_string_alloc ( LOG_PRI ( c - > syslog_priority ) , & lvl_str ) ;
2012-10-30 14:29:38 +01:00
2010-01-30 01:55:42 +01:00
fprintf ( f ,
" %sSyslogFacility: %s \n "
" %sSyslogLevel: %s \n " ,
2012-10-30 14:29:38 +01:00
prefix , strna ( fac_str ) ,
prefix , strna ( lvl_str ) ) ;
}
2010-01-30 01:55:42 +01:00
if ( c - > capabilities ) {
2014-01-01 04:35:54 +01:00
_cleanup_cap_free_charp_ char * t ;
t = cap_to_text ( c - > capabilities , NULL ) ;
if ( t )
fprintf ( f , " %sCapabilities: %s \n " , prefix , t ) ;
2010-01-30 01:55:42 +01:00
}
if ( c - > secure_bits )
fprintf ( f , " %sSecure Bits:%s%s%s%s%s%s \n " ,
prefix ,
2013-03-30 06:40:11 +01:00
( c - > secure_bits & 1 < < SECURE_KEEP_CAPS ) ? " keep-caps " : " " ,
( c - > secure_bits & 1 < < SECURE_KEEP_CAPS_LOCKED ) ? " keep-caps-locked " : " " ,
( c - > secure_bits & 1 < < SECURE_NO_SETUID_FIXUP ) ? " no-setuid-fixup " : " " ,
( c - > secure_bits & 1 < < SECURE_NO_SETUID_FIXUP_LOCKED ) ? " no-setuid-fixup-locked " : " " ,
( c - > secure_bits & 1 < < SECURE_NOROOT ) ? " noroot " : " " ,
( c - > secure_bits & 1 < < SECURE_NOROOT_LOCKED ) ? " noroot-locked " : " " ) ;
2010-01-30 01:55:42 +01:00
if ( c - > capability_bounding_set_drop ) {
2011-06-28 13:33:56 +02:00
unsigned long l ;
2011-03-18 03:13:15 +01:00
fprintf ( f , " %sCapabilityBoundingSet: " , prefix ) ;
2010-01-30 01:55:42 +01:00
2011-10-11 22:30:31 +02:00
for ( l = 0 ; l < = cap_last_cap ( ) ; l + + )
2014-12-10 03:16:14 +01:00
if ( ! ( c - > capability_bounding_set_drop & ( ( uint64_t ) 1ULL < < ( uint64_t ) l ) ) )
fprintf ( f , " %s " , strna ( capability_to_name ( l ) ) ) ;
2010-01-30 01:55:42 +01:00
fputs ( " \n " , f ) ;
}
if ( c - > user )
2010-06-18 23:25:19 +02:00
fprintf ( f , " %sUser: %s \n " , prefix , c - > user ) ;
2010-01-30 01:55:42 +01:00
if ( c - > group )
2010-06-18 23:25:19 +02:00
fprintf ( f , " %sGroup: %s \n " , prefix , c - > group ) ;
2010-01-30 01:55:42 +01:00
2010-04-21 22:15:06 +02:00
if ( strv_length ( c - > supplementary_groups ) > 0 ) {
2010-01-30 01:55:42 +01:00
fprintf ( f , " %sSupplementaryGroups: " , prefix ) ;
2010-04-21 22:15:06 +02:00
strv_fprintf ( f , c - > supplementary_groups ) ;
fputs ( " \n " , f ) ;
}
2010-01-30 01:55:42 +01:00
2010-06-16 21:54:17 +02:00
if ( c - > pam_name )
2010-06-18 23:25:19 +02:00
fprintf ( f , " %sPAMName: %s \n " , prefix , c - > pam_name ) ;
2010-06-16 21:54:17 +02:00
2010-04-21 22:15:06 +02:00
if ( strv_length ( c - > read_write_dirs ) > 0 ) {
fprintf ( f , " %sReadWriteDirs: " , prefix ) ;
strv_fprintf ( f , c - > read_write_dirs ) ;
fputs ( " \n " , f ) ;
}
if ( strv_length ( c - > read_only_dirs ) > 0 ) {
fprintf ( f , " %sReadOnlyDirs: " , prefix ) ;
strv_fprintf ( f , c - > read_only_dirs ) ;
fputs ( " \n " , f ) ;
}
2010-01-30 01:55:42 +01:00
2010-04-21 22:15:06 +02:00
if ( strv_length ( c - > inaccessible_dirs ) > 0 ) {
fprintf ( f , " %sInaccessibleDirs: " , prefix ) ;
strv_fprintf ( f , c - > inaccessible_dirs ) ;
2010-01-30 01:55:42 +01:00
fputs ( " \n " , f ) ;
}
2010-07-10 04:49:37 +02:00
2010-10-08 16:06:23 +02:00
if ( c - > utmp_id )
fprintf ( f ,
" %sUtmpIdentifier: %s \n " ,
prefix , c - > utmp_id ) ;
2014-02-06 10:05:16 +01:00
if ( c - > selinux_context )
fprintf ( f ,
2014-02-17 16:52:52 +01:00
" %sSELinuxContext: %s%s \n " ,
prefix , c - > selinux_context_ignore ? " - " : " " , c - > selinux_context ) ;
2014-02-12 18:28:21 +01:00
2014-02-19 02:15:24 +01:00
if ( c - > personality ! = 0xffffffffUL )
fprintf ( f ,
" %sPersonality: %s \n " ,
prefix , strna ( personality_to_string ( c - > personality ) ) ) ;
2014-02-12 18:28:21 +01:00
if ( c - > syscall_filter ) {
2014-02-12 18:44:40 +01:00
# ifdef HAVE_SECCOMP
2014-02-12 18:28:21 +01:00
Iterator j ;
void * id ;
bool first = true ;
2014-02-12 18:44:40 +01:00
# endif
2014-02-12 18:28:21 +01:00
fprintf ( f ,
2014-02-13 00:24:00 +01:00
" %sSystemCallFilter: " ,
2014-02-12 18:28:21 +01:00
prefix ) ;
if ( ! c - > syscall_whitelist )
fputc ( ' ~ ' , f ) ;
2014-02-12 18:44:40 +01:00
# ifdef HAVE_SECCOMP
2014-02-12 18:28:21 +01:00
SET_FOREACH ( id , c - > syscall_filter , j ) {
_cleanup_free_ char * name = NULL ;
if ( first )
first = false ;
else
fputc ( ' ' , f ) ;
2014-02-13 00:24:00 +01:00
name = seccomp_syscall_resolve_num_arch ( SCMP_ARCH_NATIVE , PTR_TO_INT ( id ) - 1 ) ;
2014-02-12 18:28:21 +01:00
fputs ( strna ( name ) , f ) ;
}
2014-02-12 18:44:40 +01:00
# endif
2014-02-12 18:28:21 +01:00
fputc ( ' \n ' , f ) ;
}
2014-02-13 00:24:00 +01:00
if ( c - > syscall_archs ) {
# ifdef HAVE_SECCOMP
Iterator j ;
void * id ;
# endif
fprintf ( f ,
" %sSystemCallArchitectures: " ,
prefix ) ;
# ifdef HAVE_SECCOMP
SET_FOREACH ( id , c - > syscall_archs , j )
fprintf ( f , " %s " , strna ( seccomp_arch_to_string ( PTR_TO_UINT32 ( id ) - 1 ) ) ) ;
# endif
fputc ( ' \n ' , f ) ;
}
2014-02-12 18:28:21 +01:00
if ( c - > syscall_errno ! = 0 )
fprintf ( f ,
" %sSystemCallErrorNumber: %s \n " ,
prefix , strna ( errno_to_name ( c - > syscall_errno ) ) ) ;
2014-02-20 16:19:44 +01:00
if ( c - > apparmor_profile )
fprintf ( f ,
" %sAppArmorProfile: %s%s \n " ,
prefix , c - > apparmor_profile_ignore ? " - " : " " , c - > apparmor_profile ) ;
2010-01-23 01:52:57 +01:00
}
2014-11-05 17:57:23 +01:00
bool exec_context_maintains_privileges ( ExecContext * c ) {
assert ( c ) ;
/* Returns true if the process forked off would run run under
* an unchanged UID or as root . */
if ( ! c - > user )
return true ;
if ( streq ( c - > user , " root " ) | | streq ( c - > user , " 0 " ) )
return true ;
return false ;
}
2010-07-04 18:49:58 +02:00
void exec_status_start ( ExecStatus * s , pid_t pid ) {
2010-01-26 04:18:44 +01:00
assert ( s ) ;
2010-01-23 01:52:57 +01:00
2010-07-04 18:49:58 +02:00
zero ( * s ) ;
s - > pid = pid ;
dual_timestamp_get ( & s - > start_timestamp ) ;
}
2011-05-18 01:07:31 +02:00
void exec_status_exit ( ExecStatus * s , ExecContext * context , pid_t pid , int code , int status ) {
2010-07-04 18:49:58 +02:00
assert ( s ) ;
2011-12-17 01:33:40 +01:00
if ( s - > pid & & s - > pid ! = pid )
2010-07-04 18:49:58 +02:00
zero ( * s ) ;
2010-01-26 04:18:44 +01:00
s - > pid = pid ;
2010-07-01 00:26:44 +02:00
dual_timestamp_get ( & s - > exit_timestamp ) ;
2010-04-10 05:03:14 +02:00
2010-01-26 04:18:44 +01:00
s - > code = code ;
s - > status = status ;
2010-10-08 16:06:23 +02:00
2011-05-18 01:07:31 +02:00
if ( context ) {
if ( context - > utmp_id )
utmp_put_dead_process ( context - > utmp_id , pid , code , status ) ;
exec_context_tty_reset ( context ) ;
}
2010-04-10 05:03:14 +02:00
}
void exec_status_dump ( ExecStatus * s , FILE * f , const char * prefix ) {
char buf [ FORMAT_TIMESTAMP_MAX ] ;
assert ( s ) ;
assert ( f ) ;
if ( s - > pid < = 0 )
return ;
2014-08-21 16:15:49 +02:00
prefix = strempty ( prefix ) ;
2010-04-10 05:03:14 +02:00
fprintf ( f ,
2013-12-30 23:22:26 +01:00
" %sPID: " PID_FMT " \n " ,
prefix , s - > pid ) ;
2010-04-10 05:03:14 +02:00
2010-07-01 00:26:44 +02:00
if ( s - > start_timestamp . realtime > 0 )
2010-04-10 05:03:14 +02:00
fprintf ( f ,
" %sStart Timestamp: %s \n " ,
2010-07-01 00:26:44 +02:00
prefix , format_timestamp ( buf , sizeof ( buf ) , s - > start_timestamp . realtime ) ) ;
2010-04-10 05:03:14 +02:00
2010-07-01 00:26:44 +02:00
if ( s - > exit_timestamp . realtime > 0 )
2010-04-10 05:03:14 +02:00
fprintf ( f ,
" %sExit Timestamp: %s \n "
" %sExit Code: %s \n "
" %sExit Status: %i \n " ,
2010-07-01 00:26:44 +02:00
prefix , format_timestamp ( buf , sizeof ( buf ) , s - > exit_timestamp . realtime ) ,
2010-04-10 05:03:14 +02:00
prefix , sigchld_code_to_string ( s - > code ) ,
prefix , s - > status ) ;
2010-01-23 01:52:57 +01:00
}
2010-01-26 07:02:51 +01:00
2010-04-15 03:11:11 +02:00
char * exec_command_line ( char * * argv ) {
2010-01-26 07:02:51 +01:00
size_t k ;
char * n , * p , * * a ;
bool first = true ;
2010-04-15 03:11:11 +02:00
assert ( argv ) ;
2010-01-26 07:02:51 +01:00
2010-01-27 02:15:54 +01:00
k = 1 ;
2010-04-15 03:11:11 +02:00
STRV_FOREACH ( a , argv )
2010-01-26 07:02:51 +01:00
k + = strlen ( * a ) + 3 ;
if ( ! ( n = new ( char , k ) ) )
return NULL ;
p = n ;
2010-04-15 03:11:11 +02:00
STRV_FOREACH ( a , argv ) {
2010-01-26 07:02:51 +01:00
if ( ! first )
* ( p + + ) = ' ' ;
else
first = false ;
if ( strpbrk ( * a , WHITESPACE ) ) {
* ( p + + ) = ' \' ' ;
p = stpcpy ( p , * a ) ;
* ( p + + ) = ' \' ' ;
} else
p = stpcpy ( p , * a ) ;
}
2010-01-27 02:15:54 +01:00
* p = 0 ;
2010-01-26 07:02:51 +01:00
/* FIXME: this doesn't really handle arguments that have
* spaces and ticks in them */
return n ;
}
void exec_command_dump ( ExecCommand * c , FILE * f , const char * prefix ) {
2014-06-24 19:00:32 +02:00
_cleanup_free_ char * cmd = NULL ;
2014-08-21 16:15:49 +02:00
const char * prefix2 ;
2010-01-26 07:02:51 +01:00
assert ( c ) ;
assert ( f ) ;
2014-08-21 16:15:49 +02:00
prefix = strempty ( prefix ) ;
prefix2 = strappenda ( prefix , " \t " ) ;
2010-01-26 07:02:51 +01:00
2010-04-15 03:11:11 +02:00
cmd = exec_command_line ( c - > argv ) ;
2010-01-26 07:02:51 +01:00
fprintf ( f ,
" %sCommand Line: %s \n " ,
prefix , cmd ? cmd : strerror ( ENOMEM ) ) ;
2010-04-10 05:03:14 +02:00
exec_status_dump ( & c - > exec_status , f , prefix2 ) ;
2010-01-26 07:02:51 +01:00
}
void exec_command_dump_list ( ExecCommand * c , FILE * f , const char * prefix ) {
assert ( f ) ;
2014-08-21 16:15:49 +02:00
prefix = strempty ( prefix ) ;
2010-01-26 07:02:51 +01:00
LIST_FOREACH ( command , c , c )
exec_command_dump ( c , f , prefix ) ;
}
2010-01-30 01:55:42 +01:00
2010-02-14 01:05:55 +01:00
void exec_command_append_list ( ExecCommand * * l , ExecCommand * e ) {
ExecCommand * end ;
assert ( l ) ;
assert ( e ) ;
if ( * l ) {
2011-02-21 15:32:17 +01:00
/* It's kind of important, that we keep the order here */
2013-10-14 06:10:14 +02:00
LIST_FIND_TAIL ( command , * l , end ) ;
LIST_INSERT_AFTER ( command , * l , end , e ) ;
2010-02-14 01:05:55 +01:00
} else
* l = e ;
}
2010-04-10 17:46:41 +02:00
int exec_command_set ( ExecCommand * c , const char * path , . . . ) {
va_list ap ;
char * * l , * p ;
assert ( c ) ;
assert ( path ) ;
va_start ( ap , path ) ;
l = strv_new_ap ( path , ap ) ;
va_end ( ap ) ;
if ( ! l )
return - ENOMEM ;
2013-10-29 19:53:43 +01:00
p = strdup ( path ) ;
if ( ! p ) {
2010-04-10 17:46:41 +02:00
strv_free ( l ) ;
return - ENOMEM ;
}
free ( c - > path ) ;
c - > path = p ;
strv_free ( c - > argv ) ;
c - > argv = l ;
return 0 ;
}
2014-09-24 14:29:05 +02:00
int exec_command_append ( ExecCommand * c , const char * path , . . . ) {
2014-09-30 11:34:01 +02:00
_cleanup_strv_free_ char * * l = NULL ;
2014-09-24 14:29:05 +02:00
va_list ap ;
int r ;
assert ( c ) ;
assert ( path ) ;
va_start ( ap , path ) ;
l = strv_new_ap ( path , ap ) ;
va_end ( ap ) ;
if ( ! l )
return - ENOMEM ;
r = strv_extend_strv ( & c - > argv , l ) ;
2014-09-30 11:34:01 +02:00
if ( r < 0 )
2014-09-24 14:29:05 +02:00
return r ;
return 0 ;
}
2013-11-27 20:23:18 +01:00
static int exec_runtime_allocate ( ExecRuntime * * rt ) {
if ( * rt )
return 0 ;
* rt = new0 ( ExecRuntime , 1 ) ;
2013-12-30 00:18:39 +01:00
if ( ! * rt )
2013-11-27 20:23:18 +01:00
return - ENOMEM ;
( * rt ) - > n_ref = 1 ;
( * rt ) - > netns_storage_socket [ 0 ] = ( * rt ) - > netns_storage_socket [ 1 ] = - 1 ;
return 0 ;
}
int exec_runtime_make ( ExecRuntime * * rt , ExecContext * c , const char * id ) {
int r ;
assert ( rt ) ;
assert ( c ) ;
assert ( id ) ;
if ( * rt )
return 1 ;
if ( ! c - > private_network & & ! c - > private_tmp )
return 0 ;
r = exec_runtime_allocate ( rt ) ;
if ( r < 0 )
return r ;
if ( c - > private_network & & ( * rt ) - > netns_storage_socket [ 0 ] < 0 ) {
if ( socketpair ( AF_UNIX , SOCK_DGRAM , 0 , ( * rt ) - > netns_storage_socket ) < 0 )
return - errno ;
}
if ( c - > private_tmp & & ! ( * rt ) - > tmp_dir ) {
r = setup_tmp_dirs ( id , & ( * rt ) - > tmp_dir , & ( * rt ) - > var_tmp_dir ) ;
if ( r < 0 )
return r ;
}
return 1 ;
}
ExecRuntime * exec_runtime_ref ( ExecRuntime * r ) {
assert ( r ) ;
assert ( r - > n_ref > 0 ) ;
r - > n_ref + + ;
return r ;
}
ExecRuntime * exec_runtime_unref ( ExecRuntime * r ) {
if ( ! r )
return NULL ;
assert ( r - > n_ref > 0 ) ;
r - > n_ref - - ;
if ( r - > n_ref < = 0 ) {
free ( r - > tmp_dir ) ;
free ( r - > var_tmp_dir ) ;
2014-03-24 03:22:44 +01:00
safe_close_pair ( r - > netns_storage_socket ) ;
2013-11-27 20:23:18 +01:00
free ( r ) ;
}
return NULL ;
}
int exec_runtime_serialize ( ExecRuntime * rt , Unit * u , FILE * f , FDSet * fds ) {
assert ( u ) ;
assert ( f ) ;
assert ( fds ) ;
if ( ! rt )
return 0 ;
if ( rt - > tmp_dir )
unit_serialize_item ( u , f , " tmp-dir " , rt - > tmp_dir ) ;
if ( rt - > var_tmp_dir )
unit_serialize_item ( u , f , " var-tmp-dir " , rt - > var_tmp_dir ) ;
if ( rt - > netns_storage_socket [ 0 ] > = 0 ) {
int copy ;
copy = fdset_put_dup ( fds , rt - > netns_storage_socket [ 0 ] ) ;
if ( copy < 0 )
return copy ;
unit_serialize_item_format ( u , f , " netns-socket-0 " , " %i " , copy ) ;
}
if ( rt - > netns_storage_socket [ 1 ] > = 0 ) {
int copy ;
copy = fdset_put_dup ( fds , rt - > netns_storage_socket [ 1 ] ) ;
if ( copy < 0 )
return copy ;
unit_serialize_item_format ( u , f , " netns-socket-1 " , " %i " , copy ) ;
}
return 0 ;
}
int exec_runtime_deserialize_item ( ExecRuntime * * rt , Unit * u , const char * key , const char * value , FDSet * fds ) {
int r ;
assert ( rt ) ;
assert ( key ) ;
assert ( value ) ;
if ( streq ( key , " tmp-dir " ) ) {
char * copy ;
r = exec_runtime_allocate ( rt ) ;
if ( r < 0 )
return r ;
copy = strdup ( value ) ;
if ( ! copy )
return log_oom ( ) ;
free ( ( * rt ) - > tmp_dir ) ;
( * rt ) - > tmp_dir = copy ;
} else if ( streq ( key , " var-tmp-dir " ) ) {
char * copy ;
r = exec_runtime_allocate ( rt ) ;
if ( r < 0 )
return r ;
copy = strdup ( value ) ;
if ( ! copy )
return log_oom ( ) ;
free ( ( * rt ) - > var_tmp_dir ) ;
( * rt ) - > var_tmp_dir = copy ;
} else if ( streq ( key , " netns-socket-0 " ) ) {
int fd ;
r = exec_runtime_allocate ( rt ) ;
if ( r < 0 )
return r ;
if ( safe_atoi ( value , & fd ) < 0 | | ! fdset_contains ( fds , fd ) )
2014-11-27 20:20:23 +01:00
log_unit_debug ( u - > id , " Failed to parse netns socket value %s " , value ) ;
2013-11-27 20:23:18 +01:00
else {
2014-03-18 19:22:43 +01:00
safe_close ( ( * rt ) - > netns_storage_socket [ 0 ] ) ;
2013-11-27 20:23:18 +01:00
( * rt ) - > netns_storage_socket [ 0 ] = fdset_remove ( fds , fd ) ;
}
} else if ( streq ( key , " netns-socket-1 " ) ) {
int fd ;
r = exec_runtime_allocate ( rt ) ;
if ( r < 0 )
return r ;
if ( safe_atoi ( value , & fd ) < 0 | | ! fdset_contains ( fds , fd ) )
2014-11-27 20:20:23 +01:00
log_unit_debug ( u - > id , " Failed to parse netns socket value %s " , value ) ;
2013-11-27 20:23:18 +01:00
else {
2014-03-18 19:22:43 +01:00
safe_close ( ( * rt ) - > netns_storage_socket [ 1 ] ) ;
2013-11-27 20:23:18 +01:00
( * rt ) - > netns_storage_socket [ 1 ] = fdset_remove ( fds , fd ) ;
}
} else
return 0 ;
return 1 ;
}
static void * remove_tmpdir_thread ( void * p ) {
_cleanup_free_ char * path = p ;
rm_rf_dangerous ( path , false , true , false ) ;
return NULL ;
}
void exec_runtime_destroy ( ExecRuntime * rt ) {
2014-03-03 17:11:39 +01:00
int r ;
2013-11-27 20:23:18 +01:00
if ( ! rt )
return ;
/* If there are multiple users of this, let's leave the stuff around */
if ( rt - > n_ref > 1 )
return ;
if ( rt - > tmp_dir ) {
log_debug ( " Spawning thread to nuke %s " , rt - > tmp_dir ) ;
2014-03-03 17:11:39 +01:00
r = asynchronous_job ( remove_tmpdir_thread , rt - > tmp_dir ) ;
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_warning_errno ( r , " Failed to nuke %s: %m " , rt - > tmp_dir ) ;
2014-03-03 17:11:39 +01:00
free ( rt - > tmp_dir ) ;
}
2013-11-27 20:23:18 +01:00
rt - > tmp_dir = NULL ;
}
if ( rt - > var_tmp_dir ) {
log_debug ( " Spawning thread to nuke %s " , rt - > var_tmp_dir ) ;
2014-03-03 17:11:39 +01:00
r = asynchronous_job ( remove_tmpdir_thread , rt - > var_tmp_dir ) ;
if ( r < 0 ) {
2014-11-28 13:19:16 +01:00
log_warning_errno ( r , " Failed to nuke %s: %m " , rt - > var_tmp_dir ) ;
2014-03-03 17:11:39 +01:00
free ( rt - > var_tmp_dir ) ;
}
2013-11-27 20:23:18 +01:00
rt - > var_tmp_dir = NULL ;
}
2014-03-24 03:22:44 +01:00
safe_close_pair ( rt - > netns_storage_socket ) ;
2013-11-27 20:23:18 +01:00
}
2010-04-13 02:06:27 +02:00
static const char * const exec_input_table [ _EXEC_INPUT_MAX ] = {
[ EXEC_INPUT_NULL ] = " null " ,
[ EXEC_INPUT_TTY ] = " tty " ,
[ EXEC_INPUT_TTY_FORCE ] = " tty-force " ,
2010-04-15 06:19:54 +02:00
[ EXEC_INPUT_TTY_FAIL ] = " tty-fail " ,
[ EXEC_INPUT_SOCKET ] = " socket "
2010-04-13 02:06:27 +02:00
} ;
2010-10-22 16:11:50 +02:00
DEFINE_STRING_TABLE_LOOKUP ( exec_input , ExecInput ) ;
2010-01-30 01:55:42 +01:00
static const char * const exec_output_table [ _EXEC_OUTPUT_MAX ] = {
2010-04-13 02:06:27 +02:00
[ EXEC_OUTPUT_INHERIT ] = " inherit " ,
2010-01-30 01:55:42 +01:00
[ EXEC_OUTPUT_NULL ] = " null " ,
2010-04-13 02:06:27 +02:00
[ EXEC_OUTPUT_TTY ] = " tty " ,
2010-01-30 01:55:42 +01:00
[ EXEC_OUTPUT_SYSLOG ] = " syslog " ,
2011-02-15 01:27:53 +01:00
[ EXEC_OUTPUT_SYSLOG_AND_CONSOLE ] = " syslog+console " ,
2010-05-19 21:49:03 +02:00
[ EXEC_OUTPUT_KMSG ] = " kmsg " ,
2011-02-15 01:27:53 +01:00
[ EXEC_OUTPUT_KMSG_AND_CONSOLE ] = " kmsg+console " ,
2012-01-05 23:54:45 +01:00
[ EXEC_OUTPUT_JOURNAL ] = " journal " ,
[ EXEC_OUTPUT_JOURNAL_AND_CONSOLE ] = " journal+console " ,
2010-04-15 06:19:54 +02:00
[ EXEC_OUTPUT_SOCKET ] = " socket "
2010-01-30 01:55:42 +01:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( exec_output , ExecOutput ) ;