2009-11-18 02:42:52 +03:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
2010-02-03 15:03:47 +03:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2009-11-18 02:42:52 +03:00
# include <assert.h>
# include <errno.h>
2010-01-19 01:50:13 +03:00
# include <string.h>
2010-01-24 02:39:29 +03:00
# include <sys/epoll.h>
# include <signal.h>
# include <sys/signalfd.h>
# include <sys/wait.h>
# include <unistd.h>
2010-04-10 19:53:17 +04:00
# include <utmpx.h>
2010-01-24 02:39:29 +03:00
# include <sys/poll.h>
2010-02-15 00:39:40 +03:00
# include <sys/reboot.h>
# include <sys/ioctl.h>
# include <linux/kd.h>
2010-03-31 18:29:55 +04:00
# include <libcgroup.h>
2010-04-13 04:06:27 +04:00
# include <termios.h>
# include <fcntl.h>
2010-04-21 05:27:44 +04:00
# include <sys/types.h>
# include <sys/stat.h>
2009-11-18 02:42:52 +03:00
# include "manager.h"
# include "hashmap.h"
# include "macro.h"
# include "strv.h"
2010-01-20 21:19:53 +03:00
# include "log.h"
2010-01-27 08:19:28 +03:00
# include "util.h"
2010-02-01 05:33:24 +03:00
# include "ratelimit.h"
2010-03-31 18:29:55 +04:00
# include "cgroup.h"
# include "mount-setup.h"
2010-04-10 19:53:17 +04:00
# include "utmp-wtmp.h"
2010-04-15 05:11:11 +04:00
# include "unit-name.h"
2010-04-18 05:08:16 +04:00
# include "dbus-unit.h"
# include "dbus-job.h"
2009-11-18 02:42:52 +03:00
2010-04-21 08:01:13 +04:00
/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
# define GC_QUEUE_ENTRIES_MAX 16
/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
# define GC_QUEUE_USEC_MAX (5*USEC_PER_SEC)
2010-04-13 04:06:27 +04:00
static int enable_special_signals ( Manager * m ) {
char fd ;
assert ( m ) ;
/* Enable that we get SIGINT on control-alt-del */
if ( reboot ( RB_DISABLE_CAD ) < 0 )
log_warning ( " Failed to enable ctrl-alt-del handling: %m " ) ;
if ( ( fd = open_terminal ( " /dev/tty0 " , O_RDWR ) ) < 0 )
log_warning ( " Failed to open /dev/tty0: %m " ) ;
else {
/* Enable that we get SIGWINCH on kbrequest */
if ( ioctl ( fd , KDSIGACCEPT , SIGWINCH ) < 0 )
log_warning ( " Failed to enable kbrequest handling: %s " , strerror ( errno ) ) ;
close_nointr_nofail ( fd ) ;
}
return 0 ;
}
2010-01-28 04:01:15 +03:00
static int manager_setup_signals ( Manager * m ) {
2010-01-24 02:39:29 +03:00
sigset_t mask ;
struct epoll_event ev ;
2010-04-13 04:36:56 +04:00
struct sigaction sa ;
2009-11-18 02:42:52 +03:00
2010-01-28 04:01:15 +03:00
assert ( m ) ;
2010-04-13 04:36:56 +04:00
/* We are not interested in SIGSTOP and friends. */
zero ( sa ) ;
sa . sa_handler = SIG_DFL ;
sa . sa_flags = SA_NOCLDSTOP | SA_RESTART ;
assert_se ( sigaction ( SIGCHLD , & sa , NULL ) = = 0 ) ;
2010-01-28 04:01:15 +03:00
assert_se ( sigemptyset ( & mask ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGCHLD ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGTERM ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGHUP ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGUSR1 ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGUSR2 ) = = 0 ) ;
2010-04-13 05:20:22 +04:00
assert_se ( sigaddset ( & mask , SIGINT ) = = 0 ) ; /* Kernel sends us this on control-alt-del */
assert_se ( sigaddset ( & mask , SIGWINCH ) = = 0 ) ; /* Kernel sends us this on kbrequest (alt-arrowup) */
assert_se ( sigaddset ( & mask , SIGPWR ) = = 0 ) ; /* Some kernel drivers and upsd send us this on power failure */
2010-01-28 04:01:15 +03:00
assert_se ( sigprocmask ( SIG_SETMASK , & mask , NULL ) = = 0 ) ;
2010-01-29 08:04:08 +03:00
m - > signal_watch . type = WATCH_SIGNAL ;
2010-01-28 04:01:15 +03:00
if ( ( m - > signal_watch . fd = signalfd ( - 1 , & mask , SFD_NONBLOCK | SFD_CLOEXEC ) ) < 0 )
return - errno ;
zero ( ev ) ;
ev . events = EPOLLIN ;
ev . data . ptr = & m - > signal_watch ;
if ( epoll_ctl ( m - > epoll_fd , EPOLL_CTL_ADD , m - > signal_watch . fd , & ev ) < 0 )
return - errno ;
2010-04-13 04:06:27 +04:00
if ( m - > running_as = = MANAGER_INIT )
return enable_special_signals ( m ) ;
2010-02-15 00:39:40 +03:00
2010-01-28 04:01:15 +03:00
return 0 ;
}
2010-02-13 03:07:02 +03:00
static char * * session_dirs ( void ) {
const char * home , * e ;
char * config_home = NULL , * data_home = NULL ;
char * * config_dirs = NULL , * * data_dirs = NULL ;
char * * r = NULL , * * t ;
/* Implement the mechanisms defined in
*
* http : //standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
*
* We look in both the config and the data dirs because we
* want to encourage that distributors ship their unit files
* as data , and allow overriding as configuration .
*/
home = getenv ( " HOME " ) ;
if ( ( e = getenv ( " XDG_CONFIG_HOME " ) ) ) {
if ( asprintf ( & config_home , " %s/systemd/session " , e ) < 0 )
goto fail ;
} else if ( home ) {
if ( asprintf ( & config_home , " %s/.config/systemd/session " , home ) < 0 )
goto fail ;
}
if ( ( e = getenv ( " XDG_CONFIG_DIRS " ) ) )
config_dirs = strv_split ( e , " : " ) ;
else
config_dirs = strv_new ( " /etc/xdg " , NULL ) ;
if ( ! config_dirs )
goto fail ;
if ( ( e = getenv ( " XDG_DATA_HOME " ) ) ) {
if ( asprintf ( & data_home , " %s/systemd/session " , e ) < 0 )
goto fail ;
} else if ( home ) {
if ( asprintf ( & data_home , " %s/.local/share/systemd/session " , home ) < 0 )
goto fail ;
}
if ( ( e = getenv ( " XDG_DATA_DIRS " ) ) )
data_dirs = strv_split ( e , " : " ) ;
else
data_dirs = strv_new ( " /usr/local/share " , " /usr/share " , NULL ) ;
if ( ! data_dirs )
goto fail ;
/* Now merge everything we found. */
if ( config_home ) {
if ( ! ( t = strv_append ( r , config_home ) ) )
goto fail ;
strv_free ( r ) ;
r = t ;
}
if ( ! ( t = strv_merge_concat ( r , config_dirs , " /systemd/session " ) ) )
goto finish ;
strv_free ( r ) ;
r = t ;
if ( ! ( t = strv_append ( r , SESSION_CONFIG_UNIT_PATH ) ) )
goto fail ;
strv_free ( r ) ;
r = t ;
if ( data_home ) {
if ( ! ( t = strv_append ( r , data_home ) ) )
goto fail ;
strv_free ( r ) ;
r = t ;
}
if ( ! ( t = strv_merge_concat ( r , data_dirs , " /systemd/session " ) ) )
goto fail ;
strv_free ( r ) ;
r = t ;
if ( ! ( t = strv_append ( r , SESSION_DATA_UNIT_PATH ) ) )
goto fail ;
strv_free ( r ) ;
r = t ;
if ( ! strv_path_make_absolute_cwd ( r ) )
goto fail ;
finish :
free ( config_home ) ;
strv_free ( config_dirs ) ;
free ( data_home ) ;
strv_free ( data_dirs ) ;
return r ;
fail :
strv_free ( r ) ;
r = NULL ;
goto finish ;
}
static int manager_find_paths ( Manager * m ) {
const char * e ;
char * t ;
2010-04-06 04:43:58 +04:00
2010-02-13 03:07:02 +03:00
assert ( m ) ;
/* First priority is whatever has been passed to us via env
* vars */
if ( ( e = getenv ( " SYSTEMD_UNIT_PATH " ) ) )
if ( ! ( m - > unit_path = split_path_and_make_absolute ( e ) ) )
return - ENOMEM ;
if ( strv_isempty ( m - > unit_path ) ) {
/* Nothing is set, so let's figure something out. */
strv_free ( m - > unit_path ) ;
if ( m - > running_as = = MANAGER_SESSION ) {
if ( ! ( m - > unit_path = session_dirs ( ) ) )
return - ENOMEM ;
} else
if ( ! ( m - > unit_path = strv_new (
SYSTEM_CONFIG_UNIT_PATH , /* /etc/systemd/system/ */
SYSTEM_DATA_UNIT_PATH , /* /lib/systemd/system/ */
NULL ) ) )
return - ENOMEM ;
}
2010-02-15 00:39:40 +03:00
if ( m - > running_as = = MANAGER_INIT ) {
2010-02-13 03:07:02 +03:00
/* /etc/init.d/ compativility does not matter to users */
if ( ( e = getenv ( " SYSTEMD_SYSVINIT_PATH " ) ) )
if ( ! ( m - > sysvinit_path = split_path_and_make_absolute ( e ) ) )
return - ENOMEM ;
if ( strv_isempty ( m - > sysvinit_path ) ) {
strv_free ( m - > sysvinit_path ) ;
if ( ! ( m - > sysvinit_path = strv_new (
SYSTEM_SYSVINIT_PATH , /* /etc/init.d/ */
NULL ) ) )
return - ENOMEM ;
}
2010-04-07 17:35:01 +04:00
if ( ( e = getenv ( " SYSTEMD_SYSVRCND_PATH " ) ) )
if ( ! ( m - > sysvrcnd_path = split_path_and_make_absolute ( e ) ) )
return - ENOMEM ;
if ( strv_isempty ( m - > sysvrcnd_path ) ) {
strv_free ( m - > sysvrcnd_path ) ;
if ( ! ( m - > sysvrcnd_path = strv_new (
SYSTEM_SYSVRCND_PATH , /* /etc/rcN.d/ */
NULL ) ) )
return - ENOMEM ;
}
2010-02-13 03:07:02 +03:00
}
strv_uniq ( m - > unit_path ) ;
strv_uniq ( m - > sysvinit_path ) ;
2010-04-07 17:35:01 +04:00
strv_uniq ( m - > sysvrcnd_path ) ;
2010-02-13 03:07:02 +03:00
assert ( ! strv_isempty ( m - > unit_path ) ) ;
if ( ! ( t = strv_join ( m - > unit_path , " \n \t " ) ) )
return - ENOMEM ;
log_debug ( " Looking for unit files in: \n \t %s " , t ) ;
free ( t ) ;
if ( ! strv_isempty ( m - > sysvinit_path ) ) {
if ( ! ( t = strv_join ( m - > sysvinit_path , " \n \t " ) ) )
return - ENOMEM ;
log_debug ( " Looking for SysV init scripts in: \n \t %s " , t ) ;
free ( t ) ;
} else
log_debug ( " Ignoring SysV init scripts. " ) ;
2010-04-07 17:35:01 +04:00
if ( ! strv_isempty ( m - > sysvrcnd_path ) ) {
if ( ! ( t = strv_join ( m - > sysvrcnd_path , " \n \t " ) ) )
return - ENOMEM ;
log_debug ( " Looking for SysV rcN.d links in: \n \t %s " , t ) ;
free ( t ) ;
} else
log_debug ( " Ignoring SysV rcN.d links. " ) ;
2010-02-13 03:07:02 +03:00
return 0 ;
}
2010-04-13 04:06:27 +04:00
int manager_new ( ManagerRunningAs running_as , bool confirm_spawn , Manager * * _m ) {
2010-01-28 04:01:15 +03:00
Manager * m ;
2010-03-31 18:29:55 +04:00
int r = - ENOMEM ;
assert ( _m ) ;
2010-04-07 01:55:42 +04:00
assert ( running_as > = 0 ) ;
assert ( running_as < _MANAGER_RUNNING_AS_MAX ) ;
2010-01-28 04:01:15 +03:00
2009-11-18 02:42:52 +03:00
if ( ! ( m = new0 ( Manager , 1 ) ) )
2010-03-31 18:29:55 +04:00
return - ENOMEM ;
2009-11-18 02:42:52 +03:00
2010-04-10 19:53:17 +04:00
m - > boot_timestamp = now ( CLOCK_REALTIME ) ;
2010-04-07 01:55:42 +04:00
m - > running_as = running_as ;
2010-04-13 04:06:27 +04:00
m - > confirm_spawn = confirm_spawn ;
2010-04-16 01:16:16 +04:00
m - > name_data_slot = - 1 ;
2010-04-21 05:27:44 +04:00
m - > exit_code = _MANAGER_EXIT_CODE_INVALID ;
2010-04-13 04:06:27 +04:00
2010-04-17 01:24:39 +04:00
m - > signal_watch . fd = m - > mount_watch . fd = m - > udev_watch . fd = m - > epoll_fd = m - > dev_autofs_fd = - 1 ;
2010-02-01 05:33:24 +03:00
m - > current_job_id = 1 ; /* start as id #1, so that we can leave #0 around as "null-like" value */
2010-01-24 02:39:29 +03:00
2010-01-26 23:39:06 +03:00
if ( ! ( m - > units = hashmap_new ( string_hash_func , string_compare_func ) ) )
2009-11-18 02:42:52 +03:00
goto fail ;
if ( ! ( m - > jobs = hashmap_new ( trivial_hash_func , trivial_compare_func ) ) )
goto fail ;
2010-01-20 04:12:51 +03:00
if ( ! ( m - > transaction_jobs = hashmap_new ( trivial_hash_func , trivial_compare_func ) ) )
2009-11-18 02:42:52 +03:00
goto fail ;
2010-01-24 02:39:29 +03:00
if ( ! ( m - > watch_pids = hashmap_new ( trivial_hash_func , trivial_compare_func ) ) )
goto fail ;
2010-03-31 18:29:55 +04:00
if ( ! ( m - > cgroup_bondings = hashmap_new ( string_hash_func , string_compare_func ) ) )
goto fail ;
2010-04-16 01:16:16 +04:00
if ( ! ( m - > watch_bus = hashmap_new ( string_hash_func , string_compare_func ) ) )
goto fail ;
2010-01-24 02:39:29 +03:00
if ( ( m - > epoll_fd = epoll_create1 ( EPOLL_CLOEXEC ) ) < 0 )
goto fail ;
2010-03-31 18:29:55 +04:00
if ( ( r = manager_find_paths ( m ) ) < 0 )
2010-02-15 00:39:40 +03:00
goto fail ;
2010-03-31 18:29:55 +04:00
if ( ( r = manager_setup_signals ( m ) ) < 0 )
goto fail ;
if ( ( r = manager_setup_cgroup ( m ) ) < 0 )
2010-01-24 02:39:29 +03:00
goto fail ;
2010-04-06 18:32:07 +04:00
/* Try to connect to the busses, if possible. */
if ( ( r = bus_init_system ( m ) ) < 0 | |
( r = bus_init_api ( m ) ) < 0 )
2010-02-01 05:33:24 +03:00
goto fail ;
2010-03-31 18:29:55 +04:00
* _m = m ;
return 0 ;
2009-11-18 02:42:52 +03:00
fail :
manager_free ( m ) ;
2010-03-31 18:29:55 +04:00
return r ;
2009-11-18 02:42:52 +03:00
}
2010-04-06 04:43:58 +04:00
static unsigned manager_dispatch_cleanup_queue ( Manager * m ) {
Meta * meta ;
unsigned n = 0 ;
assert ( m ) ;
while ( ( meta = m - > cleanup_queue ) ) {
assert ( meta - > in_cleanup_queue ) ;
unit_free ( UNIT ( meta ) ) ;
n + + ;
}
return n ;
}
2010-04-21 08:01:13 +04:00
static void unit_gc_sweep ( Unit * u , int gc_marker ) {
Iterator i ;
Unit * other ;
assert ( u ) ;
if ( u - > meta . gc_marker = = gc_marker | |
u - > meta . gc_marker = = - gc_marker )
return ;
2010-04-22 04:41:14 +04:00
if ( u - > meta . in_cleanup_queue )
2010-04-21 08:01:13 +04:00
goto bad ;
if ( unit_check_gc ( u ) )
goto good ;
SET_FOREACH ( other , u - > meta . dependencies [ UNIT_REFERENCED_BY ] , i ) {
unit_gc_sweep ( other , gc_marker ) ;
if ( other - > meta . gc_marker = = gc_marker )
goto good ;
}
bad :
/* So there is no reason to keep this unit around, hence let's get rid of it */
u - > meta . gc_marker = - gc_marker ;
return ;
good :
u - > meta . gc_marker = gc_marker ;
}
static unsigned manager_dispatch_gc_queue ( Manager * m ) {
Meta * meta ;
unsigned n = 0 ;
int gc_marker ;
assert ( m ) ;
if ( ( m - > n_in_gc_queue < GC_QUEUE_ENTRIES_MAX ) & &
( m - > gc_queue_timestamp < = 0 | |
( m - > gc_queue_timestamp + GC_QUEUE_USEC_MAX ) > now ( CLOCK_MONOTONIC ) ) )
return 0 ;
log_debug ( " Running GC... " ) ;
2010-04-22 04:41:14 +04:00
gc_marker = + + m - > gc_marker ;
if ( m - > gc_marker < 0 )
m - > gc_marker = 1 ;
2010-04-21 08:01:13 +04:00
while ( ( meta = m - > gc_queue ) ) {
assert ( meta - > in_gc_queue ) ;
LIST_REMOVE ( Meta , gc_queue , m - > gc_queue , meta ) ;
meta - > in_gc_queue = false ;
n + + ;
unit_gc_sweep ( UNIT ( meta ) , gc_marker ) ;
if ( meta - > gc_marker = = - gc_marker ) {
log_debug ( " Collecting %s " , meta - > id ) ;
unit_add_to_cleanup_queue ( UNIT ( meta ) ) ;
}
}
m - > n_in_gc_queue = 0 ;
m - > gc_queue_timestamp = 0 ;
return n ;
}
2010-04-21 05:27:44 +04:00
static void manager_clear_jobs_and_units ( Manager * m ) {
2010-01-20 04:12:51 +03:00
Job * j ;
2010-04-21 05:27:44 +04:00
Unit * u ;
2009-11-18 02:42:52 +03:00
assert ( m ) ;
2010-01-26 23:39:06 +03:00
while ( ( j = hashmap_first ( m - > transaction_jobs ) ) )
2010-01-20 04:12:51 +03:00
job_free ( j ) ;
2010-01-26 23:39:06 +03:00
while ( ( u = hashmap_first ( m - > units ) ) )
unit_free ( u ) ;
2010-04-21 05:27:44 +04:00
}
void manager_free ( Manager * m ) {
UnitType c ;
2010-01-26 23:39:06 +03:00
2010-04-21 05:27:44 +04:00
assert ( m ) ;
manager_clear_jobs_and_units ( m ) ;
2010-04-06 04:43:58 +04:00
2010-01-28 08:45:44 +03:00
for ( c = 0 ; c < _UNIT_TYPE_MAX ; c + + )
if ( unit_vtable [ c ] - > shutdown )
unit_vtable [ c ] - > shutdown ( m ) ;
2010-04-21 05:27:44 +04:00
/* If we reexecute ourselves, we keep the root cgroup
* around */
manager_shutdown_cgroup ( m , m - > exit_code ! = MANAGER_REEXECUTE ) ;
2010-03-31 18:29:55 +04:00
2010-04-06 18:32:07 +04:00
bus_done_api ( m ) ;
bus_done_system ( m ) ;
2010-02-01 05:33:24 +03:00
2010-01-26 23:39:06 +03:00
hashmap_free ( m - > units ) ;
2009-11-18 02:42:52 +03:00
hashmap_free ( m - > jobs ) ;
2010-01-20 04:12:51 +03:00
hashmap_free ( m - > transaction_jobs ) ;
2010-01-24 02:39:29 +03:00
hashmap_free ( m - > watch_pids ) ;
2010-04-16 01:16:16 +04:00
hashmap_free ( m - > watch_bus ) ;
2010-01-24 02:39:29 +03:00
if ( m - > epoll_fd > = 0 )
2010-04-21 05:27:44 +04:00
close_nointr_nofail ( m - > epoll_fd ) ;
2010-01-27 06:31:52 +03:00
if ( m - > signal_watch . fd > = 0 )
2010-04-21 05:27:44 +04:00
close_nointr_nofail ( m - > signal_watch . fd ) ;
2009-11-18 02:42:52 +03:00
2010-02-13 03:07:02 +03:00
strv_free ( m - > unit_path ) ;
strv_free ( m - > sysvinit_path ) ;
2010-04-07 17:35:01 +04:00
strv_free ( m - > sysvrcnd_path ) ;
2010-02-13 03:07:02 +03:00
2010-03-31 18:29:55 +04:00
free ( m - > cgroup_controller ) ;
free ( m - > cgroup_hierarchy ) ;
hashmap_free ( m - > cgroup_bondings ) ;
2009-11-18 02:42:52 +03:00
free ( m ) ;
}
2010-04-21 05:27:44 +04:00
int manager_enumerate ( Manager * m ) {
int r = 0 , q ;
2010-01-29 05:18:09 +03:00
UnitType c ;
assert ( m ) ;
2010-04-21 05:27:44 +04:00
/* Let's ask every type to load all units from disk/kernel
* that it might know */
2010-01-29 05:18:09 +03:00
for ( c = 0 ; c < _UNIT_TYPE_MAX ; c + + )
if ( unit_vtable [ c ] - > enumerate )
2010-04-21 05:27:44 +04:00
if ( ( q = unit_vtable [ c ] - > enumerate ( m ) ) < 0 )
r = q ;
2010-01-29 05:18:09 +03:00
manager_dispatch_load_queue ( m ) ;
2010-04-21 05:27:44 +04:00
return r ;
}
int manager_coldplug ( Manager * m ) {
int r = 0 , q ;
Iterator i ;
Unit * u ;
char * k ;
assert ( m ) ;
2010-01-29 05:18:09 +03:00
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY ( u , k , m - > units , i ) {
/* ignore aliases */
2010-04-15 05:11:11 +04:00
if ( u - > meta . id ! = k )
2010-01-29 05:18:09 +03:00
continue ;
if ( UNIT_VTABLE ( u ) - > coldplug )
2010-04-21 05:27:44 +04:00
if ( ( q = UNIT_VTABLE ( u ) - > coldplug ( u ) ) < 0 )
r = q ;
2010-01-29 05:18:09 +03:00
}
2010-04-21 05:27:44 +04:00
return r ;
}
int manager_startup ( Manager * m , FILE * serialization , FDSet * fds ) {
int r , q ;
assert ( m ) ;
/* First, enumerate what we can from all config files */
r = manager_enumerate ( m ) ;
/* Second, deserialize if there is something to deserialize */
if ( serialization )
if ( ( q = manager_deserialize ( m , serialization , fds ) ) < 0 )
r = q ;
/* Third, fire things up! */
if ( ( q = manager_coldplug ( m ) ) < 0 )
r = q ;
2010-04-10 19:53:17 +04:00
/* Now that the initial devices are available, let's see if we
* can write the utmp file */
manager_write_utmp_reboot ( m ) ;
2010-04-21 05:27:44 +04:00
return r ;
2010-01-29 05:18:09 +03:00
}
2010-04-06 04:43:58 +04:00
static void transaction_delete_job ( Manager * m , Job * j , bool delete_dependencies ) {
2010-01-20 22:47:49 +03:00
assert ( m ) ;
assert ( j ) ;
2010-01-21 02:51:37 +03:00
/* Deletes one job from the transaction */
2010-04-06 04:43:58 +04:00
manager_transaction_unlink_job ( m , j , delete_dependencies ) ;
2010-01-20 22:47:49 +03:00
2010-01-26 21:25:02 +03:00
if ( ! j - > installed )
2010-01-20 22:47:49 +03:00
job_free ( j ) ;
}
2010-01-26 23:39:06 +03:00
static void transaction_delete_unit ( Manager * m , Unit * u ) {
2010-01-21 02:51:37 +03:00
Job * j ;
2010-01-26 23:39:06 +03:00
/* Deletes all jobs associated with a certain unit from the
2010-01-21 02:51:37 +03:00
* transaction */
2010-01-26 23:39:06 +03:00
while ( ( j = hashmap_get ( m - > transaction_jobs , u ) ) )
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , j , true ) ;
2010-01-21 02:51:37 +03:00
}
2010-01-27 03:43:18 +03:00
static void transaction_clean_dependencies ( Manager * m ) {
Iterator i ;
Job * j ;
assert ( m ) ;
/* Drops all dependencies of all installed jobs */
HASHMAP_FOREACH ( j , m - > jobs , i ) {
while ( j - > subject_list )
job_dependency_free ( j - > subject_list ) ;
while ( j - > object_list )
job_dependency_free ( j - > object_list ) ;
}
assert ( ! m - > transaction_anchor ) ;
}
2010-01-19 06:15:20 +03:00
static void transaction_abort ( Manager * m ) {
Job * j ;
assert ( m ) ;
2010-01-20 04:12:51 +03:00
while ( ( j = hashmap_first ( m - > transaction_jobs ) ) )
2010-01-26 21:25:02 +03:00
if ( j - > installed )
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , j , true ) ;
2010-01-20 04:12:51 +03:00
else
job_free ( j ) ;
assert ( hashmap_isempty ( m - > transaction_jobs ) ) ;
2010-01-27 03:43:18 +03:00
transaction_clean_dependencies ( m ) ;
2010-01-20 04:12:51 +03:00
}
static void transaction_find_jobs_that_matter_to_anchor ( Manager * m , Job * j , unsigned generation ) {
JobDependency * l ;
assert ( m ) ;
2010-01-26 23:39:06 +03:00
/* A recursive sweep through the graph that marks all units
2010-01-21 02:51:37 +03:00
* that matter to the anchor job , i . e . are directly or
* indirectly a dependency of the anchor job via paths that
* are fully marked as mattering . */
2010-01-26 09:02:51 +03:00
if ( j )
l = j - > subject_list ;
else
l = m - > transaction_anchor ;
LIST_FOREACH ( subject , l , l ) {
2010-01-20 04:12:51 +03:00
/* This link does not matter */
if ( ! l - > matters )
continue ;
2010-01-26 23:39:06 +03:00
/* This unit has already been marked */
2010-01-20 04:12:51 +03:00
if ( l - > object - > generation = = generation )
continue ;
l - > object - > matters_to_anchor = true ;
l - > object - > generation = generation ;
transaction_find_jobs_that_matter_to_anchor ( m , l - > object , generation ) ;
}
}
2010-01-20 07:03:52 +03:00
static void transaction_merge_and_delete_job ( Manager * m , Job * j , Job * other , JobType t ) {
2010-01-20 04:12:51 +03:00
JobDependency * l , * last ;
assert ( j ) ;
assert ( other ) ;
2010-01-26 23:39:06 +03:00
assert ( j - > unit = = other - > unit ) ;
2010-01-26 21:25:02 +03:00
assert ( ! j - > installed ) ;
2010-01-20 04:12:51 +03:00
2010-01-21 02:51:37 +03:00
/* Merges 'other' into 'j' and then deletes j. */
2010-01-20 04:12:51 +03:00
j - > type = t ;
j - > state = JOB_WAITING ;
2010-04-15 05:11:11 +04:00
j - > override = j - > override | | other - > override ;
2010-01-20 04:12:51 +03:00
j - > matters_to_anchor = j - > matters_to_anchor | | other - > matters_to_anchor ;
/* Patch us in as new owner of the JobDependency objects */
last = NULL ;
2010-01-26 09:02:51 +03:00
LIST_FOREACH ( subject , l , other - > subject_list ) {
2010-01-20 04:12:51 +03:00
assert ( l - > subject = = other ) ;
l - > subject = j ;
last = l ;
}
/* Merge both lists */
if ( last ) {
last - > subject_next = j - > subject_list ;
if ( j - > subject_list )
j - > subject_list - > subject_prev = last ;
j - > subject_list = other - > subject_list ;
}
/* Patch us in as new owner of the JobDependency objects */
last = NULL ;
2010-01-26 09:02:51 +03:00
LIST_FOREACH ( object , l , other - > object_list ) {
2010-01-20 04:12:51 +03:00
assert ( l - > object = = other ) ;
l - > object = j ;
last = l ;
}
/* Merge both lists */
if ( last ) {
last - > object_next = j - > object_list ;
if ( j - > object_list )
j - > object_list - > object_prev = last ;
j - > object_list = other - > object_list ;
}
/* Kill the other job */
other - > subject_list = NULL ;
other - > object_list = NULL ;
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , other , true ) ;
2010-01-20 04:12:51 +03:00
}
2010-01-23 03:52:57 +03:00
static int delete_one_unmergeable_job ( Manager * m , Job * j ) {
2010-01-21 02:51:37 +03:00
Job * k ;
assert ( j ) ;
/* Tries to delete one item in the linked list
* j - > transaction_next - > transaction_next - > . . . that conflicts
* whith another one , in an attempt to make an inconsistent
* transaction work . */
/* We rely here on the fact that if a merged with b does not
* merge with c , either a or b merge with c neither */
2010-01-26 06:18:44 +03:00
LIST_FOREACH ( transaction , j , j )
LIST_FOREACH ( transaction , k , j - > transaction_next ) {
2010-01-21 02:51:37 +03:00
Job * d ;
/* Is this one mergeable? Then skip it */
2010-01-23 03:52:57 +03:00
if ( job_type_is_mergeable ( j - > type , k - > type ) )
2010-01-21 02:51:37 +03:00
continue ;
/* Ok, we found two that conflict, let's see if we can
* drop one of them */
if ( ! j - > matters_to_anchor )
d = j ;
else if ( ! k - > matters_to_anchor )
d = k ;
else
return - ENOEXEC ;
/* Ok, we can drop one, so let's do so. */
2010-04-15 05:11:11 +04:00
log_debug ( " Trying to fix job merging by deleting job %s/%s " , d - > unit - > meta . id , job_type_to_string ( d - > type ) ) ;
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , d , true ) ;
2010-01-21 02:51:37 +03:00
return 0 ;
}
return - EINVAL ;
}
2010-01-20 04:12:51 +03:00
static int transaction_merge_jobs ( Manager * m ) {
2010-01-19 06:15:20 +03:00
Job * j ;
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-20 04:12:51 +03:00
int r ;
assert ( m ) ;
2010-01-21 02:51:37 +03:00
/* First step, check whether any of the jobs for one specific
* task conflict . If so , try to drop one of them . */
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-21 02:51:37 +03:00
JobType t ;
Job * k ;
t = j - > type ;
2010-01-26 06:18:44 +03:00
LIST_FOREACH ( transaction , k , j - > transaction_next ) {
2010-01-21 02:51:37 +03:00
if ( ( r = job_type_merge ( & t , k - > type ) ) > = 0 )
continue ;
/* OK, we could not merge all jobs for this
* action . Let ' s see if we can get rid of one
* of them */
2010-01-23 03:52:57 +03:00
if ( ( r = delete_one_unmergeable_job ( m , j ) ) > = 0 )
2010-01-21 02:51:37 +03:00
/* Ok, we managed to drop one, now
* let ' s ask our callers to call us
* again after garbage collecting */
return - EAGAIN ;
/* We couldn't merge anything. Failure */
return r ;
}
}
/* Second step, merge the jobs. */
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-20 04:12:51 +03:00
JobType t = j - > type ;
Job * k ;
2010-01-21 04:59:12 +03:00
/* Merge all transactions */
2010-01-26 06:18:44 +03:00
LIST_FOREACH ( transaction , k , j - > transaction_next )
2010-01-21 02:51:37 +03:00
assert_se ( job_type_merge ( & t , k - > type ) = = 0 ) ;
2010-01-20 04:12:51 +03:00
2010-01-23 03:52:57 +03:00
/* If an active job is mergeable, merge it too */
2010-01-26 23:39:06 +03:00
if ( j - > unit - > meta . job )
job_type_merge ( & t , j - > unit - > meta . job - > type ) ; /* Might fail. Which is OK */
2010-01-21 04:59:12 +03:00
2010-01-20 04:12:51 +03:00
while ( ( k = j - > transaction_next ) ) {
2010-01-26 21:25:02 +03:00
if ( j - > installed ) {
2010-01-20 07:03:52 +03:00
transaction_merge_and_delete_job ( m , k , j , t ) ;
2010-01-20 04:12:51 +03:00
j = k ;
} else
2010-01-20 07:03:52 +03:00
transaction_merge_and_delete_job ( m , j , k , t ) ;
2010-01-20 04:12:51 +03:00
}
assert ( ! j - > transaction_next ) ;
assert ( ! j - > transaction_prev ) ;
}
2010-01-20 07:03:52 +03:00
return 0 ;
2010-01-20 04:12:51 +03:00
}
2010-04-06 04:43:58 +04:00
static void transaction_drop_redundant ( Manager * m ) {
bool again ;
assert ( m ) ;
/* Goes through the transaction and removes all jobs that are
* a noop */
do {
Job * j ;
Iterator i ;
again = false ;
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
bool changes_something = false ;
Job * k ;
LIST_FOREACH ( transaction , k , j ) {
if ( ! job_is_anchor ( k ) & &
job_type_is_redundant ( k - > type , unit_active_state ( k - > unit ) ) )
continue ;
changes_something = true ;
break ;
}
if ( changes_something )
continue ;
2010-04-15 05:11:11 +04:00
log_debug ( " Found redundant job %s/%s, dropping. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , j , false ) ;
again = true ;
break ;
}
} while ( again ) ;
}
2010-01-26 23:39:06 +03:00
static bool unit_matters_to_anchor ( Unit * u , Job * j ) {
assert ( u ) ;
2010-01-21 02:51:37 +03:00
assert ( ! j - > transaction_prev ) ;
2010-01-26 23:39:06 +03:00
/* Checks whether at least one of the jobs for this unit
2010-01-21 02:51:37 +03:00
* matters to the anchor . */
2010-01-26 06:18:44 +03:00
LIST_FOREACH ( transaction , j , j )
2010-01-21 02:51:37 +03:00
if ( j - > matters_to_anchor )
return true ;
return false ;
}
2010-01-20 04:12:51 +03:00
static int transaction_verify_order_one ( Manager * m , Job * j , Job * from , unsigned generation ) {
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-26 23:39:06 +03:00
Unit * u ;
2010-01-19 06:15:20 +03:00
int r ;
2010-01-20 04:12:51 +03:00
assert ( m ) ;
assert ( j ) ;
2010-01-21 02:51:37 +03:00
assert ( ! j - > transaction_prev ) ;
/* Does a recursive sweep through the ordering graph, looking
* for a cycle . If we find cycle we try to break it . */
2010-01-20 04:12:51 +03:00
2010-01-20 07:03:52 +03:00
/* Did we find a cycle? */
2010-01-20 04:12:51 +03:00
if ( j - > marker & & j - > generation = = generation ) {
Job * k ;
/* So, we already have been here. We have a
2010-01-21 02:51:37 +03:00
* cycle . Let ' s try to break it . We go backwards in
* our path and try to find a suitable job to
* remove . We use the marker to find our way back ,
* since smart how we are we stored our way back in
* there . */
2010-01-20 04:12:51 +03:00
2010-04-15 05:11:11 +04:00
log_debug ( " Found ordering cycle on %s/%s " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 06:11:36 +03:00
2010-01-20 04:12:51 +03:00
for ( k = from ; k ; k = ( k - > generation = = generation ? k - > marker : NULL ) ) {
2010-01-21 02:51:37 +03:00
2010-04-15 05:11:11 +04:00
log_debug ( " Walked on cycle path to %s/%s " , k - > unit - > meta . id , job_type_to_string ( k - > type ) ) ;
2010-01-29 06:11:36 +03:00
2010-01-26 21:25:02 +03:00
if ( ! k - > installed & &
2010-01-26 23:39:06 +03:00
! unit_matters_to_anchor ( k - > unit , k ) ) {
2010-01-21 02:51:37 +03:00
/* Ok, we can drop this one, so let's
* do so . */
2010-04-15 05:11:11 +04:00
log_debug ( " Breaking order cycle by deleting job %s/%s " , k - > unit - > meta . id , job_type_to_string ( k - > type ) ) ;
2010-01-26 23:39:06 +03:00
transaction_delete_unit ( m , k - > unit ) ;
2010-01-20 04:12:51 +03:00
return - EAGAIN ;
}
/* Check if this in fact was the beginning of
2010-01-20 07:03:52 +03:00
* the cycle */
2010-01-20 04:12:51 +03:00
if ( k = = j )
break ;
}
2010-01-29 06:11:36 +03:00
log_debug ( " Unable to break cycle " ) ;
2010-01-21 02:51:37 +03:00
return - ENOEXEC ;
2010-01-20 04:12:51 +03:00
}
2010-01-21 02:51:37 +03:00
/* Make the marker point to where we come from, so that we can
* find our way backwards if we want to break a cycle */
2010-01-20 04:12:51 +03:00
j - > marker = from ;
j - > generation = generation ;
2010-01-21 02:51:37 +03:00
/* We assume that the the dependencies are bidirectional, and
2010-01-26 23:39:06 +03:00
* hence can ignore UNIT_AFTER */
SET_FOREACH ( u , j - > unit - > meta . dependencies [ UNIT_BEFORE ] , i ) {
2010-01-20 04:12:51 +03:00
Job * o ;
2010-01-26 23:39:06 +03:00
/* Is there a job for this unit? */
if ( ! ( o = hashmap_get ( m - > transaction_jobs , u ) ) )
2010-01-21 02:51:37 +03:00
/* Ok, there is no job for this in the
* transaction , but maybe there is already one
* running ? */
2010-01-26 23:39:06 +03:00
if ( ! ( o = u - > meta . job ) )
2010-01-20 04:12:51 +03:00
continue ;
if ( ( r = transaction_verify_order_one ( m , o , j , generation ) ) < 0 )
return r ;
}
2010-01-29 06:11:36 +03:00
/* Ok, let's backtrack, and remember that this entry is not on
* our path anymore . */
j - > marker = NULL ;
2010-01-20 04:12:51 +03:00
return 0 ;
}
static int transaction_verify_order ( Manager * m , unsigned * generation ) {
2010-01-21 02:51:37 +03:00
Job * j ;
int r ;
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-21 02:51:37 +03:00
2010-01-20 04:12:51 +03:00
assert ( m ) ;
assert ( generation ) ;
2010-01-21 02:51:37 +03:00
/* Check if the ordering graph is cyclic. If it is, try to fix
* that up by dropping one of the jobs . */
2010-01-20 04:12:51 +03:00
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i )
2010-01-21 02:51:37 +03:00
if ( ( r = transaction_verify_order_one ( m , j , NULL , ( * generation ) + + ) ) < 0 )
return r ;
2010-01-20 04:12:51 +03:00
return 0 ;
}
static void transaction_collect_garbage ( Manager * m ) {
bool again ;
assert ( m ) ;
2010-01-21 02:51:37 +03:00
/* Drop jobs that are not required by any other job */
2010-01-20 04:12:51 +03:00
do {
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-20 04:12:51 +03:00
Job * j ;
again = false ;
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-20 04:12:51 +03:00
if ( j - > object_list )
continue ;
2010-04-15 05:11:11 +04:00
log_debug ( " Garbage collecting job %s/%s " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , j , true ) ;
2010-01-20 04:12:51 +03:00
again = true ;
break ;
}
} while ( again ) ;
}
2010-04-22 04:42:59 +04:00
static int transaction_is_destructive ( Manager * m ) {
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-20 04:12:51 +03:00
Job * j ;
2010-01-19 06:15:20 +03:00
assert ( m ) ;
2010-01-20 04:12:51 +03:00
/* Checks whether applying this transaction means that
* existing jobs would be replaced */
2010-01-19 06:15:20 +03:00
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-21 04:59:12 +03:00
/* Assume merged */
assert ( ! j - > transaction_prev ) ;
assert ( ! j - > transaction_next ) ;
2010-01-26 23:39:06 +03:00
if ( j - > unit - > meta . job & &
j - > unit - > meta . job ! = j & &
! job_type_is_superset ( j - > type , j - > unit - > meta . job - > type ) )
2010-01-20 04:12:51 +03:00
return - EEXIST ;
2010-01-21 04:59:12 +03:00
}
2010-01-19 06:15:20 +03:00
2010-01-20 04:12:51 +03:00
return 0 ;
}
2010-01-21 04:59:12 +03:00
static void transaction_minimize_impact ( Manager * m ) {
bool again ;
assert ( m ) ;
/* Drops all unnecessary jobs that reverse already active jobs
* or that stop a running service . */
do {
Job * j ;
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-21 04:59:12 +03:00
again = false ;
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
LIST_FOREACH ( transaction , j , j ) {
2010-01-29 06:26:30 +03:00
bool stops_running_service , changes_existing_job ;
2010-01-21 04:59:12 +03:00
/* If it matters, we shouldn't drop it */
if ( j - > matters_to_anchor )
continue ;
/* Would this stop a running service?
* Would this change an existing job ?
* If so , let ' s drop this entry */
2010-01-29 06:26:30 +03:00
stops_running_service =
j - > type = = JOB_STOP & & UNIT_IS_ACTIVE_OR_ACTIVATING ( unit_active_state ( j - > unit ) ) ;
changes_existing_job =
j - > unit - > meta . job & & job_type_is_conflicting ( j - > type , j - > unit - > meta . job - > state ) ;
if ( ! stops_running_service & & ! changes_existing_job )
2010-01-21 04:59:12 +03:00
continue ;
2010-01-29 06:26:30 +03:00
if ( stops_running_service )
2010-04-15 05:11:11 +04:00
log_debug ( " %s/%s would stop a running service. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 06:26:30 +03:00
if ( changes_existing_job )
2010-04-15 05:11:11 +04:00
log_debug ( " %s/%s would change existing job. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 06:26:30 +03:00
2010-01-21 04:59:12 +03:00
/* Ok, let's get rid of this */
2010-04-15 05:11:11 +04:00
log_debug ( " Deleting %s/%s to minimize impact. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 06:26:30 +03:00
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , j , true ) ;
2010-01-21 04:59:12 +03:00
again = true ;
break ;
}
if ( again )
break ;
}
} while ( again ) ;
}
2010-04-22 04:42:59 +04:00
static int transaction_apply ( Manager * m ) {
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-20 04:12:51 +03:00
Job * j ;
int r ;
2010-01-21 02:51:37 +03:00
/* Moves the transaction jobs to the set of active jobs */
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-21 04:59:12 +03:00
/* Assume merged */
assert ( ! j - > transaction_prev ) ;
assert ( ! j - > transaction_next ) ;
2010-01-26 21:25:02 +03:00
if ( j - > installed )
2010-01-20 04:12:51 +03:00
continue ;
if ( ( r = hashmap_put ( m - > jobs , UINT32_TO_PTR ( j - > id ) , j ) ) < 0 )
2010-01-19 06:15:20 +03:00
goto rollback ;
}
2010-01-20 04:12:51 +03:00
while ( ( j = hashmap_steal_first ( m - > transaction_jobs ) ) ) {
2010-01-26 21:25:02 +03:00
if ( j - > installed )
2010-01-20 04:12:51 +03:00
continue ;
2010-01-26 23:39:06 +03:00
if ( j - > unit - > meta . job )
job_free ( j - > unit - > meta . job ) ;
2010-01-19 06:15:20 +03:00
2010-01-26 23:39:06 +03:00
j - > unit - > meta . job = j ;
2010-01-26 21:25:02 +03:00
j - > installed = true ;
2010-01-19 06:15:20 +03:00
2010-01-20 04:12:51 +03:00
/* We're fully installed. Now let's free data we don't
* need anymore . */
assert ( ! j - > transaction_next ) ;
assert ( ! j - > transaction_prev ) ;
2010-02-05 02:38:41 +03:00
job_add_to_run_queue ( j ) ;
job_add_to_dbus_queue ( j ) ;
2010-01-27 03:39:24 +03:00
}
/* As last step, kill all remaining job dependencies. */
2010-01-27 03:43:18 +03:00
transaction_clean_dependencies ( m ) ;
2010-01-21 02:51:37 +03:00
2010-01-19 06:15:20 +03:00
return 0 ;
rollback :
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-26 21:25:02 +03:00
if ( j - > installed )
2010-01-20 04:12:51 +03:00
continue ;
hashmap_remove ( m - > jobs , UINT32_TO_PTR ( j - > id ) ) ;
}
return r ;
}
static int transaction_activate ( Manager * m , JobMode mode ) {
int r ;
unsigned generation = 1 ;
assert ( m ) ;
/* This applies the changes recorded in transaction_jobs to
* the actual list of jobs , if possible . */
/* First step: figure out which jobs matter */
transaction_find_jobs_that_matter_to_anchor ( m , NULL , generation + + ) ;
2010-01-21 04:59:12 +03:00
/* Second step: Try not to stop any running services if
* we don ' t have to . Don ' t try to reverse running
* jobs if we don ' t have to . */
transaction_minimize_impact ( m ) ;
2010-04-06 04:43:58 +04:00
/* Third step: Drop redundant jobs */
transaction_drop_redundant ( m ) ;
2010-01-21 02:51:37 +03:00
for ( ; ; ) {
2010-04-06 04:43:58 +04:00
/* Fourth step: Let's remove unneeded jobs that might
2010-01-21 02:51:37 +03:00
* be lurking . */
transaction_collect_garbage ( m ) ;
2010-01-20 04:12:51 +03:00
2010-04-06 04:43:58 +04:00
/* Fifth step: verify order makes sense and correct
2010-01-21 02:51:37 +03:00
* cycles if necessary and possible */
if ( ( r = transaction_verify_order ( m , & generation ) ) > = 0 )
break ;
2010-01-20 04:12:51 +03:00
2010-01-29 06:11:36 +03:00
if ( r ! = - EAGAIN ) {
log_debug ( " Requested transaction contains an unfixable cyclic ordering dependency: %s " , strerror ( - r ) ) ;
2010-01-21 02:51:37 +03:00
goto rollback ;
2010-01-29 06:11:36 +03:00
}
2010-01-20 04:12:51 +03:00
2010-01-21 02:51:37 +03:00
/* Let's see if the resulting transaction ordering
* graph is still cyclic . . . */
}
for ( ; ; ) {
2010-04-06 04:43:58 +04:00
/* Sixth step: let's drop unmergeable entries if
2010-01-21 02:51:37 +03:00
* necessary and possible , merge entries we can
* merge */
if ( ( r = transaction_merge_jobs ( m ) ) > = 0 )
break ;
2010-01-29 06:11:36 +03:00
if ( r ! = - EAGAIN ) {
log_debug ( " Requested transaction contains unmergable jobs: %s " , strerror ( - r ) ) ;
2010-01-21 02:51:37 +03:00
goto rollback ;
2010-01-29 06:11:36 +03:00
}
2010-01-21 02:51:37 +03:00
2010-04-06 04:43:58 +04:00
/* Seventh step: an entry got dropped, let's garbage
2010-01-21 02:51:37 +03:00
* collect its dependencies . */
transaction_collect_garbage ( m ) ;
/* Let's see if the resulting transaction still has
2010-01-23 03:52:57 +03:00
* unmergeable entries . . . */
2010-01-21 02:51:37 +03:00
}
2010-04-06 04:43:58 +04:00
/* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
transaction_drop_redundant ( m ) ;
/* Ninth step: check whether we can actually apply this */
2010-01-20 04:12:51 +03:00
if ( mode = = JOB_FAIL )
2010-04-22 04:42:59 +04:00
if ( ( r = transaction_is_destructive ( m ) ) < 0 ) {
2010-01-29 06:11:36 +03:00
log_debug ( " Requested transaction contradicts existing jobs: %s " , strerror ( - r ) ) ;
2010-01-20 04:12:51 +03:00
goto rollback ;
2010-01-29 06:11:36 +03:00
}
2010-01-20 04:12:51 +03:00
2010-04-06 04:43:58 +04:00
/* Tenth step: apply changes */
2010-04-22 04:42:59 +04:00
if ( ( r = transaction_apply ( m ) ) < 0 ) {
2010-01-29 06:11:36 +03:00
log_debug ( " Failed to apply transaction: %s " , strerror ( - r ) ) ;
2010-01-20 04:12:51 +03:00
goto rollback ;
2010-01-29 06:11:36 +03:00
}
2010-01-20 04:12:51 +03:00
assert ( hashmap_isempty ( m - > transaction_jobs ) ) ;
assert ( ! m - > transaction_anchor ) ;
return 0 ;
2010-01-19 06:15:20 +03:00
2010-01-20 04:12:51 +03:00
rollback :
2010-01-19 06:15:20 +03:00
transaction_abort ( m ) ;
return r ;
}
2010-04-15 05:11:11 +04:00
static Job * transaction_add_one_job ( Manager * m , JobType type , Unit * unit , bool override , bool * is_new ) {
2010-01-20 04:12:51 +03:00
Job * j , * f ;
2009-11-18 02:42:52 +03:00
int r ;
assert ( m ) ;
2010-01-26 23:39:06 +03:00
assert ( unit ) ;
2009-11-18 02:42:52 +03:00
2010-01-20 04:12:51 +03:00
/* Looks for an axisting prospective job and returns that. If
* it doesn ' t exist it is created and added to the prospective
* jobs list . */
2009-11-18 02:42:52 +03:00
2010-01-26 23:39:06 +03:00
f = hashmap_get ( m - > transaction_jobs , unit ) ;
2009-11-18 02:42:52 +03:00
2010-01-26 06:18:44 +03:00
LIST_FOREACH ( transaction , j , f ) {
2010-01-26 23:39:06 +03:00
assert ( j - > unit = = unit ) ;
2009-11-18 02:42:52 +03:00
2010-01-20 04:12:51 +03:00
if ( j - > type = = type ) {
if ( is_new )
* is_new = false ;
return j ;
}
}
2009-11-18 02:42:52 +03:00
2010-01-26 23:39:06 +03:00
if ( unit - > meta . job & & unit - > meta . job - > type = = type )
j = unit - > meta . job ;
else if ( ! ( j = job_new ( m , type , unit ) ) )
2010-01-20 04:12:51 +03:00
return NULL ;
2009-11-18 02:42:52 +03:00
2010-01-20 04:12:51 +03:00
j - > generation = 0 ;
j - > marker = NULL ;
j - > matters_to_anchor = false ;
2010-04-15 05:11:11 +04:00
j - > override = override ;
2009-11-18 02:42:52 +03:00
2010-01-26 06:18:44 +03:00
LIST_PREPEND ( Job , transaction , f , j ) ;
2010-01-26 23:39:06 +03:00
if ( ( r = hashmap_replace ( m - > transaction_jobs , unit , f ) ) < 0 ) {
2010-01-26 06:18:44 +03:00
job_free ( j ) ;
return NULL ;
}
2010-01-20 04:12:51 +03:00
if ( is_new )
* is_new = true ;
2009-11-18 02:42:52 +03:00
2010-04-15 05:11:11 +04:00
log_debug ( " Added job %s/%s to transaction. " , unit - > meta . id , job_type_to_string ( type ) ) ;
2010-04-06 04:43:58 +04:00
2010-01-20 04:12:51 +03:00
return j ;
}
2010-01-19 06:15:20 +03:00
2010-04-06 04:43:58 +04:00
void manager_transaction_unlink_job ( Manager * m , Job * j , bool delete_dependencies ) {
2010-01-20 04:12:51 +03:00
assert ( m ) ;
assert ( j ) ;
2010-01-19 06:15:20 +03:00
2010-01-20 04:12:51 +03:00
if ( j - > transaction_prev )
j - > transaction_prev - > transaction_next = j - > transaction_next ;
else if ( j - > transaction_next )
2010-01-26 23:39:06 +03:00
hashmap_replace ( m - > transaction_jobs , j - > unit , j - > transaction_next ) ;
2010-01-20 04:12:51 +03:00
else
2010-01-26 23:39:06 +03:00
hashmap_remove_value ( m - > transaction_jobs , j - > unit , j ) ;
2010-01-20 04:12:51 +03:00
if ( j - > transaction_next )
j - > transaction_next - > transaction_prev = j - > transaction_prev ;
j - > transaction_prev = j - > transaction_next = NULL ;
while ( j - > subject_list )
job_dependency_free ( j - > subject_list ) ;
2010-01-20 21:20:15 +03:00
while ( j - > object_list ) {
Job * other = j - > object_list - > matters ? j - > object_list - > subject : NULL ;
2010-01-20 04:12:51 +03:00
job_dependency_free ( j - > object_list ) ;
2010-01-20 21:20:15 +03:00
2010-04-06 04:43:58 +04:00
if ( other & & delete_dependencies ) {
2010-01-23 03:52:57 +03:00
log_debug ( " Deleting job %s/%s as dependency of job %s/%s " ,
2010-04-15 05:11:11 +04:00
other - > unit - > meta . id , job_type_to_string ( other - > type ) ,
j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-04-06 04:43:58 +04:00
transaction_delete_job ( m , other , delete_dependencies ) ;
2010-01-20 21:20:15 +03:00
}
}
2010-01-20 04:12:51 +03:00
}
2010-04-15 05:11:11 +04:00
static int transaction_add_job_and_dependencies (
Manager * m ,
JobType type ,
Unit * unit ,
Job * by ,
bool matters ,
bool override ,
Job * * _ret ) {
2010-01-20 04:12:51 +03:00
Job * ret ;
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-26 23:39:06 +03:00
Unit * dep ;
2010-01-20 04:12:51 +03:00
int r ;
bool is_new ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
2010-01-26 23:39:06 +03:00
assert ( unit ) ;
2010-01-20 04:12:51 +03:00
2010-01-26 23:39:06 +03:00
if ( unit - > meta . load_state ! = UNIT_LOADED )
2010-01-20 21:20:41 +03:00
return - EINVAL ;
2010-01-26 23:39:06 +03:00
if ( ! unit_job_is_applicable ( unit , type ) )
2010-01-21 05:26:34 +03:00
return - EBADR ;
2010-01-20 04:12:51 +03:00
/* First add the job. */
2010-04-15 05:11:11 +04:00
if ( ! ( ret = transaction_add_one_job ( m , type , unit , override , & is_new ) ) )
2010-01-20 04:12:51 +03:00
return - ENOMEM ;
/* Then, add a link to the job. */
if ( ! job_dependency_new ( by , ret , matters ) )
return - ENOMEM ;
if ( is_new ) {
/* Finally, recursively add in all dependencies. */
if ( type = = JOB_START | | type = = JOB_RELOAD_OR_START ) {
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUIRES ] , i )
2010-04-15 05:11:11 +04:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , true , override , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
2010-04-15 05:11:11 +04:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUIRES_OVERRIDABLE ] , i )
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , ! override , override , NULL ) ) < 0 & & r ! = - EBADR )
log_warning ( " Cannot add dependency job for unit %s, ignoring: %s " , dep - > meta . id , strerror ( - r ) ) ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_WANTS ] , i )
2010-04-15 05:11:11 +04:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , false , false , NULL ) ) < 0 )
log_warning ( " Cannot add dependency job for unit %s, ignoring: %s " , dep - > meta . id , strerror ( - r ) ) ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUISITE ] , i )
2010-04-15 05:11:11 +04:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_VERIFY_ACTIVE , dep , ret , true , override , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
2010-04-15 05:11:11 +04:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUISITE_OVERRIDABLE ] , i )
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_VERIFY_ACTIVE , dep , ret , ! override , override , NULL ) ) < 0 & & r ! = - EBADR )
log_warning ( " Cannot add dependency job for unit %s, ignoring: %s " , dep - > meta . id , strerror ( - r ) ) ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_CONFLICTS ] , i )
2010-04-15 05:11:11 +04:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_STOP , dep , ret , true , override , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
} else if ( type = = JOB_STOP | | type = = JOB_RESTART | | type = = JOB_TRY_RESTART ) {
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUIRED_BY ] , i )
2010-04-15 05:11:11 +04:00
if ( ( r = transaction_add_job_and_dependencies ( m , type , dep , ret , true , override , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
}
/* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
}
2009-11-18 02:42:52 +03:00
2010-02-12 04:21:08 +03:00
if ( _ret )
* _ret = ret ;
2009-11-18 02:42:52 +03:00
return 0 ;
fail :
2010-01-20 04:12:51 +03:00
return r ;
}
2010-04-22 04:42:59 +04:00
static int transaction_add_isolate_jobs ( Manager * m ) {
Iterator i ;
Unit * u ;
char * k ;
int r ;
assert ( m ) ;
HASHMAP_FOREACH_KEY ( u , k , m - > units , i ) {
/* ignore aliases */
if ( u - > meta . id ! = k )
continue ;
if ( UNIT_VTABLE ( u ) - > no_isolate )
continue ;
/* No need to stop inactive jobs */
if ( unit_active_state ( u ) = = UNIT_INACTIVE )
continue ;
/* Is there already something listed for this? */
if ( hashmap_get ( m - > transaction_jobs , u ) )
continue ;
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_STOP , u , NULL , true , false , NULL ) ) < 0 )
log_warning ( " Cannot add isolate job for unit %s, ignoring: %s " , u - > meta . id , strerror ( - r ) ) ;
}
return 0 ;
}
2010-04-15 05:11:11 +04:00
int manager_add_job ( Manager * m , JobType type , Unit * unit , JobMode mode , bool override , Job * * _ret ) {
2010-01-20 04:12:51 +03:00
int r ;
Job * ret ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
2010-01-26 23:39:06 +03:00
assert ( unit ) ;
2010-01-20 04:12:51 +03:00
assert ( mode < _JOB_MODE_MAX ) ;
2009-11-18 02:42:52 +03:00
2010-04-22 04:42:59 +04:00
if ( mode = = JOB_ISOLATE & & type ! = JOB_START )
return - EINVAL ;
2010-04-15 05:11:11 +04:00
log_debug ( " Trying to enqueue job %s/%s " , unit - > meta . id , job_type_to_string ( type ) ) ;
2010-01-29 06:11:36 +03:00
2010-04-15 05:11:11 +04:00
if ( ( r = transaction_add_job_and_dependencies ( m , type , unit , NULL , true , override , & ret ) ) < 0 ) {
2010-01-19 06:15:20 +03:00
transaction_abort ( m ) ;
2010-01-20 04:12:51 +03:00
return r ;
}
2010-01-19 06:15:20 +03:00
2010-04-22 04:42:59 +04:00
if ( mode = = JOB_ISOLATE )
if ( ( r = transaction_add_isolate_jobs ( m ) ) < 0 ) {
transaction_abort ( m ) ;
return r ;
}
2010-01-20 04:12:51 +03:00
if ( ( r = transaction_activate ( m , mode ) ) < 0 )
return r ;
2010-04-15 05:11:11 +04:00
log_debug ( " Enqueued job %s/%s as %u " , unit - > meta . id , job_type_to_string ( type ) , ( unsigned ) ret - > id ) ;
2010-01-29 05:18:09 +03:00
2010-01-20 04:12:51 +03:00
if ( _ret )
* _ret = ret ;
2009-11-18 02:42:52 +03:00
2010-01-20 04:12:51 +03:00
return 0 ;
}
2009-11-18 02:42:52 +03:00
2010-04-15 05:11:11 +04:00
int manager_add_job_by_name ( Manager * m , JobType type , const char * name , JobMode mode , bool override , Job * * _ret ) {
2010-04-13 03:59:06 +04:00
Unit * unit ;
int r ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
assert ( name ) ;
assert ( mode < _JOB_MODE_MAX ) ;
2010-04-15 05:11:11 +04:00
if ( ( r = manager_load_unit ( m , name , NULL , & unit ) ) < 0 )
2010-04-13 03:59:06 +04:00
return r ;
2010-04-15 05:11:11 +04:00
return manager_add_job ( m , type , unit , mode , override , _ret ) ;
2010-04-13 03:59:06 +04:00
}
2009-11-18 02:42:52 +03:00
Job * manager_get_job ( Manager * m , uint32_t id ) {
assert ( m ) ;
return hashmap_get ( m - > jobs , UINT32_TO_PTR ( id ) ) ;
}
2010-01-26 23:39:06 +03:00
Unit * manager_get_unit ( Manager * m , const char * name ) {
2009-11-18 02:42:52 +03:00
assert ( m ) ;
assert ( name ) ;
2010-01-26 23:39:06 +03:00
return hashmap_get ( m - > units , name ) ;
2009-11-18 02:42:52 +03:00
}
2010-02-05 02:38:41 +03:00
unsigned manager_dispatch_load_queue ( Manager * m ) {
2009-11-18 02:42:52 +03:00
Meta * meta ;
2010-02-05 02:38:41 +03:00
unsigned n = 0 ;
2009-11-18 02:42:52 +03:00
assert ( m ) ;
2009-11-19 04:52:17 +03:00
/* Make sure we are not run recursively */
if ( m - > dispatching_load_queue )
2010-02-05 02:38:41 +03:00
return 0 ;
2009-11-19 04:52:17 +03:00
m - > dispatching_load_queue = true ;
2010-01-26 23:39:06 +03:00
/* Dispatches the load queue. Takes a unit from the queue and
2009-11-18 02:42:52 +03:00
* tries to load its data until the queue is empty */
while ( ( meta = m - > load_queue ) ) {
2010-01-26 06:18:44 +03:00
assert ( meta - > in_load_queue ) ;
2010-01-26 23:39:06 +03:00
unit_load ( UNIT ( meta ) ) ;
2010-02-05 02:38:41 +03:00
n + + ;
2009-11-18 02:42:52 +03:00
}
2009-11-19 04:52:17 +03:00
m - > dispatching_load_queue = false ;
2010-02-05 02:38:41 +03:00
return n ;
2009-11-18 02:42:52 +03:00
}
2010-04-15 05:11:11 +04:00
int manager_load_unit ( Manager * m , const char * name , const char * path , Unit * * _ret ) {
2010-01-26 23:39:06 +03:00
Unit * ret ;
2009-11-18 02:42:52 +03:00
int r ;
assert ( m ) ;
2010-04-15 05:11:11 +04:00
assert ( name | | path ) ;
2009-11-18 02:42:52 +03:00
2009-11-19 04:52:17 +03:00
/* This will load the service information files, but not actually
2010-01-27 02:15:56 +03:00
* start any services or anything . */
2010-04-15 05:11:11 +04:00
if ( path & & ! is_path ( path ) )
return - EINVAL ;
if ( ! name )
name = file_name_from_path ( path ) ;
if ( ! unit_name_is_valid ( name ) )
return - EINVAL ;
2009-11-18 02:42:52 +03:00
2010-01-26 23:39:06 +03:00
if ( ( ret = manager_get_unit ( m , name ) ) ) {
2010-01-26 06:18:44 +03:00
* _ret = ret ;
return 0 ;
}
2009-11-18 02:42:52 +03:00
2010-01-26 23:39:06 +03:00
if ( ! ( ret = unit_new ( m ) ) )
2009-11-18 02:42:52 +03:00
return - ENOMEM ;
2010-04-15 05:11:11 +04:00
if ( path )
2010-02-14 03:01:10 +03:00
if ( ! ( ret - > meta . fragment_path = strdup ( path ) ) ) {
2010-01-27 02:15:56 +03:00
unit_free ( ret ) ;
return - ENOMEM ;
}
2010-01-26 23:39:06 +03:00
if ( ( r = unit_add_name ( ret , name ) ) < 0 ) {
unit_free ( ret ) ;
2010-01-21 02:51:37 +03:00
return r ;
2009-11-18 02:42:52 +03:00
}
2010-01-26 23:39:06 +03:00
unit_add_to_load_queue ( ret ) ;
2010-02-05 02:38:41 +03:00
unit_add_to_dbus_queue ( ret ) ;
2010-01-29 05:18:09 +03:00
manager_dispatch_load_queue ( m ) ;
2009-11-18 02:42:52 +03:00
2010-04-15 05:11:11 +04:00
if ( _ret )
* _ret = unit_follow_merge ( ret ) ;
2009-11-18 02:42:52 +03:00
return 0 ;
}
2010-01-19 02:22:34 +03:00
2010-01-20 06:02:39 +03:00
void manager_dump_jobs ( Manager * s , FILE * f , const char * prefix ) {
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-19 02:22:34 +03:00
Job * j ;
assert ( s ) ;
assert ( f ) ;
2010-01-26 06:18:44 +03:00
HASHMAP_FOREACH ( j , s - > jobs , i )
2010-01-20 06:02:39 +03:00
job_dump ( j , f , prefix ) ;
2010-01-19 02:22:34 +03:00
}
2010-01-26 23:39:06 +03:00
void manager_dump_units ( Manager * s , FILE * f , const char * prefix ) {
2010-01-26 06:18:44 +03:00
Iterator i ;
2010-01-26 23:39:06 +03:00
Unit * u ;
2010-01-19 06:15:20 +03:00
const char * t ;
2010-01-19 02:22:34 +03:00
assert ( s ) ;
assert ( f ) ;
2010-01-26 23:39:06 +03:00
HASHMAP_FOREACH_KEY ( u , t , s - > units , i )
2010-04-15 05:11:11 +04:00
if ( u - > meta . id = = t )
2010-01-26 23:39:06 +03:00
unit_dump ( u , f , prefix ) ;
2010-01-19 02:22:34 +03:00
}
2010-01-20 07:03:52 +03:00
void manager_clear_jobs ( Manager * m ) {
Job * j ;
assert ( m ) ;
transaction_abort ( m ) ;
while ( ( j = hashmap_first ( m - > jobs ) ) )
job_free ( j ) ;
}
2010-01-24 00:56:47 +03:00
2010-02-05 02:38:41 +03:00
unsigned manager_dispatch_run_queue ( Manager * m ) {
2010-01-24 00:56:47 +03:00
Job * j ;
2010-02-05 02:38:41 +03:00
unsigned n = 0 ;
2010-01-24 00:56:47 +03:00
2010-01-26 06:18:44 +03:00
if ( m - > dispatching_run_queue )
2010-02-05 02:38:41 +03:00
return 0 ;
2010-01-26 06:18:44 +03:00
m - > dispatching_run_queue = true ;
2010-01-24 02:39:29 +03:00
2010-01-26 06:18:44 +03:00
while ( ( j = m - > run_queue ) ) {
2010-01-26 21:25:02 +03:00
assert ( j - > installed ) ;
2010-01-26 06:18:44 +03:00
assert ( j - > in_run_queue ) ;
job_run_and_invalidate ( j ) ;
2010-02-05 02:38:41 +03:00
n + + ;
2010-01-24 02:39:29 +03:00
}
2010-01-26 06:18:44 +03:00
m - > dispatching_run_queue = false ;
2010-02-05 02:38:41 +03:00
return n ;
}
unsigned manager_dispatch_dbus_queue ( Manager * m ) {
Job * j ;
Meta * meta ;
unsigned n = 0 ;
assert ( m ) ;
if ( m - > dispatching_dbus_queue )
return 0 ;
m - > dispatching_dbus_queue = true ;
while ( ( meta = m - > dbus_unit_queue ) ) {
2010-04-06 04:43:58 +04:00
assert ( meta - > in_dbus_queue ) ;
2010-02-05 02:38:41 +03:00
2010-04-06 04:43:58 +04:00
bus_unit_send_change_signal ( UNIT ( meta ) ) ;
2010-02-05 02:38:41 +03:00
n + + ;
}
while ( ( j = m - > dbus_job_queue ) ) {
assert ( j - > in_dbus_queue ) ;
bus_job_send_change_signal ( j ) ;
n + + ;
}
m - > dispatching_dbus_queue = false ;
return n ;
2010-01-24 02:39:29 +03:00
}
2010-01-26 06:18:44 +03:00
static int manager_dispatch_sigchld ( Manager * m ) {
2010-01-24 02:39:29 +03:00
assert ( m ) ;
for ( ; ; ) {
siginfo_t si ;
2010-01-26 23:39:06 +03:00
Unit * u ;
2010-01-24 02:39:29 +03:00
zero ( si ) ;
2010-04-13 04:05:27 +04:00
/* First we call waitd() for a PID and do not reap the
* zombie . That way we can still access / proc / $ PID for
* it while it is a zombie . */
if ( waitid ( P_ALL , 0 , & si , WEXITED | WNOHANG | WNOWAIT ) < 0 ) {
2010-01-27 06:31:52 +03:00
if ( errno = = ECHILD )
break ;
2010-04-13 04:05:27 +04:00
if ( errno = = EINTR )
continue ;
2010-01-24 02:39:29 +03:00
return - errno ;
2010-01-27 06:31:52 +03:00
}
2010-01-24 02:39:29 +03:00
2010-04-13 04:05:27 +04:00
if ( si . si_pid < = 0 )
2010-01-24 02:39:29 +03:00
break ;
2010-04-13 04:10:17 +04:00
if ( si . si_code = = CLD_EXITED | | si . si_code = = CLD_KILLED | | si . si_code = = CLD_DUMPED ) {
2010-04-13 04:05:27 +04:00
char * name = NULL ;
get_process_name ( si . si_pid , & name ) ;
log_debug ( " Got SIGCHLD for process %llu (%s) " , ( unsigned long long ) si . si_pid , strna ( name ) ) ;
free ( name ) ;
}
/* And now, we actually reap the zombie. */
if ( waitid ( P_PID , si . si_pid , & si , WEXITED ) < 0 ) {
if ( errno = = EINTR )
continue ;
return - errno ;
}
2010-01-26 06:18:44 +03:00
if ( si . si_code ! = CLD_EXITED & & si . si_code ! = CLD_KILLED & & si . si_code ! = CLD_DUMPED )
continue ;
2010-04-13 04:05:27 +04:00
log_debug ( " Child %llu died (code=%s, status=%i/%s) " ,
( long long unsigned ) si . si_pid ,
sigchld_code_to_string ( si . si_code ) ,
si . si_status ,
strna ( si . si_code = = CLD_EXITED ? exit_status_to_string ( si . si_status ) : strsignal ( si . si_status ) ) ) ;
2010-01-27 06:31:52 +03:00
2010-01-26 23:39:06 +03:00
if ( ! ( u = hashmap_remove ( m - > watch_pids , UINT32_TO_PTR ( si . si_pid ) ) ) )
2010-01-24 02:39:29 +03:00
continue ;
2010-04-15 05:11:11 +04:00
log_debug ( " Child %llu belongs to %s " , ( long long unsigned ) si . si_pid , u - > meta . id ) ;
2010-04-07 05:19:00 +04:00
2010-01-26 23:39:06 +03:00
UNIT_VTABLE ( u ) - > sigchld_event ( u , si . si_pid , si . si_code , si . si_status ) ;
2010-01-24 02:39:29 +03:00
}
return 0 ;
}
2010-04-13 03:59:06 +04:00
static void manager_start_target ( Manager * m , const char * name ) {
int r ;
if ( ( r = manager_add_job_by_name ( m , JOB_START , name , JOB_REPLACE , true , NULL ) ) < 0 )
log_error ( " Failed to enqueue %s job: %s " , name , strerror ( - r ) ) ;
}
2010-04-21 05:27:44 +04:00
static int manager_process_signal_fd ( Manager * m ) {
2010-01-24 02:39:29 +03:00
ssize_t n ;
struct signalfd_siginfo sfsi ;
bool sigchld = false ;
assert ( m ) ;
for ( ; ; ) {
2010-01-27 06:31:52 +03:00
if ( ( n = read ( m - > signal_watch . fd , & sfsi , sizeof ( sfsi ) ) ) ! = sizeof ( sfsi ) ) {
2010-01-24 02:39:29 +03:00
if ( n > = 0 )
return - EIO ;
if ( errno = = EAGAIN )
2010-01-27 06:31:52 +03:00
break ;
2010-01-24 02:39:29 +03:00
return - errno ;
}
2010-01-27 06:36:30 +03:00
switch ( sfsi . ssi_signo ) {
2010-04-13 04:05:27 +04:00
case SIGCHLD :
2010-01-24 02:39:29 +03:00
sigchld = true ;
2010-01-27 06:36:30 +03:00
break ;
case SIGINT :
2010-01-27 07:31:53 +03:00
case SIGTERM :
2010-02-13 03:17:08 +03:00
2010-04-13 03:59:06 +04:00
if ( m - > running_as = = MANAGER_INIT ) {
manager_start_target ( m , SPECIAL_CTRL_ALT_DEL_TARGET ) ;
2010-02-13 03:17:08 +03:00
break ;
}
2010-04-21 05:27:44 +04:00
m - > exit_code = MANAGER_EXIT ;
2010-04-13 03:59:06 +04:00
return 0 ;
2010-02-13 03:17:08 +03:00
2010-04-13 03:59:06 +04:00
case SIGWINCH :
2010-02-13 03:17:08 +03:00
2010-04-13 03:59:06 +04:00
if ( m - > running_as = = MANAGER_INIT )
manager_start_target ( m , SPECIAL_KBREQUEST_TARGET ) ;
2010-02-13 03:17:08 +03:00
2010-04-13 03:59:06 +04:00
/* This is a nop on non-init */
break ;
2010-02-13 03:17:08 +03:00
2010-04-13 03:59:06 +04:00
case SIGPWR :
if ( m - > running_as = = MANAGER_INIT )
manager_start_target ( m , SPECIAL_SIGPWR_TARGET ) ;
2010-02-13 03:17:08 +03:00
2010-04-13 03:59:06 +04:00
/* This is a nop on non-init */
2010-02-13 03:17:08 +03:00
break ;
2010-01-27 07:31:53 +03:00
2010-04-06 23:54:57 +04:00
case SIGUSR1 :
manager_dump_units ( m , stdout , " \t " ) ;
manager_dump_jobs ( m , stdout , " \t " ) ;
break ;
2010-04-13 05:20:22 +04:00
case SIGUSR2 : {
Unit * u ;
u = manager_get_unit ( m , SPECIAL_DBUS_SERVICE ) ;
if ( ! u | | UNIT_IS_ACTIVE_OR_RELOADING ( unit_active_state ( u ) ) ) {
log_info ( " Trying to reconnect to bus... " ) ;
bus_init_system ( m ) ;
bus_init_api ( m ) ;
}
if ( ! u | | ! UNIT_IS_ACTIVE_OR_ACTIVATING ( unit_active_state ( u ) ) ) {
log_info ( " Loading D-Bus service... " ) ;
manager_start_target ( m , SPECIAL_DBUS_SERVICE ) ;
}
break ;
}
2010-04-21 05:27:44 +04:00
case SIGHUP :
m - > exit_code = MANAGER_RELOAD ;
break ;
2010-01-27 07:31:53 +03:00
default :
log_info ( " Got unhandled signal <%s>. " , strsignal ( sfsi . ssi_signo ) ) ;
2010-01-27 06:36:30 +03:00
}
2010-01-24 02:39:29 +03:00
}
if ( sigchld )
2010-01-26 06:18:44 +03:00
return manager_dispatch_sigchld ( m ) ;
return 0 ;
}
2010-04-21 05:27:44 +04:00
static int process_event ( Manager * m , struct epoll_event * ev ) {
2010-01-26 06:18:44 +03:00
int r ;
2010-01-27 06:31:52 +03:00
Watch * w ;
2010-01-26 06:18:44 +03:00
assert ( m ) ;
assert ( ev ) ;
2010-01-27 06:31:52 +03:00
assert ( w = ev - > data . ptr ) ;
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
switch ( w - > type ) {
2010-01-26 06:18:44 +03:00
2010-01-29 08:04:08 +03:00
case WATCH_SIGNAL :
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
/* An incoming signal? */
2010-01-29 08:45:59 +03:00
if ( ev - > events ! = EPOLLIN )
2010-01-27 06:31:52 +03:00
return - EINVAL ;
2010-01-26 06:18:44 +03:00
2010-04-21 05:27:44 +04:00
if ( ( r = manager_process_signal_fd ( m ) ) < 0 )
2010-01-27 06:31:52 +03:00
return r ;
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
break ;
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
case WATCH_FD :
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
/* Some fd event, to be dispatched to the units */
2010-02-01 05:33:24 +03:00
UNIT_VTABLE ( w - > data . unit ) - > fd_event ( w - > data . unit , w - > fd , ev - > events , w ) ;
2010-01-27 06:31:52 +03:00
break ;
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
case WATCH_TIMER : {
uint64_t v ;
ssize_t k ;
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
/* Some timer event, to be dispatched to the units */
2010-02-05 02:40:39 +03:00
if ( ( k = read ( w - > fd , & v , sizeof ( v ) ) ) ! = sizeof ( v ) ) {
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
if ( k < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) )
break ;
2010-01-26 06:18:44 +03:00
2010-01-27 06:31:52 +03:00
return k < 0 ? - errno : - EIO ;
2010-01-26 06:18:44 +03:00
}
2010-02-01 05:33:24 +03:00
UNIT_VTABLE ( w - > data . unit ) - > timer_event ( w - > data . unit , v , w ) ;
2010-01-27 06:31:52 +03:00
break ;
}
2010-01-29 08:04:08 +03:00
case WATCH_MOUNT :
/* Some mount table change, intended for the mount subsystem */
mount_fd_event ( m , ev - > events ) ;
break ;
2010-01-29 08:45:59 +03:00
case WATCH_UDEV :
/* Some notification from udev, intended for the device subsystem */
device_fd_event ( m , ev - > events ) ;
break ;
2010-02-01 05:33:24 +03:00
case WATCH_DBUS_WATCH :
bus_watch_event ( m , w , ev - > events ) ;
break ;
case WATCH_DBUS_TIMEOUT :
bus_timeout_event ( m , w , ev - > events ) ;
break ;
2010-01-27 06:31:52 +03:00
default :
assert_not_reached ( " Unknown epoll event type. " ) ;
2010-01-26 06:18:44 +03:00
}
2010-01-24 02:39:29 +03:00
return 0 ;
}
int manager_loop ( Manager * m ) {
int r ;
2010-02-01 05:33:24 +03:00
RATELIMIT_DEFINE ( rl , 1 * USEC_PER_SEC , 1000 ) ;
2010-01-24 02:39:29 +03:00
assert ( m ) ;
2010-04-21 05:27:44 +04:00
m - > exit_code = MANAGER_RUNNING ;
2010-01-24 02:39:29 +03:00
2010-04-21 05:27:44 +04:00
while ( m - > exit_code = = MANAGER_RUNNING ) {
2010-01-28 00:40:10 +03:00
struct epoll_event event ;
int n ;
2010-01-24 02:39:29 +03:00
2010-02-01 05:33:24 +03:00
if ( ! ratelimit_test ( & rl ) ) {
/* Yay, something is going seriously wrong, pause a little */
log_warning ( " Looping too fast. Throttling execution a little. " ) ;
sleep ( 1 ) ;
}
2010-04-06 04:43:58 +04:00
if ( manager_dispatch_cleanup_queue ( m ) > 0 )
continue ;
2010-04-21 08:01:13 +04:00
if ( manager_dispatch_gc_queue ( m ) > 0 )
continue ;
2010-02-05 02:38:41 +03:00
if ( manager_dispatch_load_queue ( m ) > 0 )
continue ;
2010-01-26 06:18:44 +03:00
2010-02-05 02:38:41 +03:00
if ( manager_dispatch_run_queue ( m ) > 0 )
continue ;
if ( bus_dispatch ( m ) > 0 )
continue ;
if ( manager_dispatch_dbus_queue ( m ) > 0 )
2010-02-01 05:33:24 +03:00
continue ;
2010-01-28 00:40:10 +03:00
if ( ( n = epoll_wait ( m - > epoll_fd , & event , 1 , - 1 ) ) < 0 ) {
2010-01-24 02:39:29 +03:00
if ( errno = = - EINTR )
continue ;
return - errno ;
}
2010-01-28 00:40:10 +03:00
assert ( n = = 1 ) ;
2010-01-27 06:36:30 +03:00
2010-04-21 05:27:44 +04:00
if ( ( r = process_event ( m , & event ) ) < 0 )
2010-01-28 00:40:10 +03:00
return r ;
2010-04-21 05:27:44 +04:00
}
2010-01-28 00:40:10 +03:00
2010-04-21 05:27:44 +04:00
return m - > exit_code ;
2010-01-24 00:56:47 +03:00
}
2010-02-01 05:33:24 +03:00
int manager_get_unit_from_dbus_path ( Manager * m , const char * s , Unit * * _u ) {
char * n ;
Unit * u ;
assert ( m ) ;
assert ( s ) ;
assert ( _u ) ;
if ( ! startswith ( s , " /org/freedesktop/systemd1/unit/ " ) )
return - EINVAL ;
if ( ! ( n = bus_path_unescape ( s + 31 ) ) )
return - ENOMEM ;
u = manager_get_unit ( m , n ) ;
free ( n ) ;
if ( ! u )
return - ENOENT ;
* _u = u ;
return 0 ;
}
2010-02-02 14:42:08 +03:00
int manager_get_job_from_dbus_path ( Manager * m , const char * s , Job * * _j ) {
Job * j ;
unsigned id ;
int r ;
assert ( m ) ;
assert ( s ) ;
assert ( _j ) ;
if ( ! startswith ( s , " /org/freedesktop/systemd1/job/ " ) )
return - EINVAL ;
if ( ( r = safe_atou ( s + 30 , & id ) ) < 0 )
return r ;
if ( ! ( j = manager_get_job ( m , id ) ) )
return - ENOENT ;
* _j = j ;
return 0 ;
}
2010-02-12 23:57:39 +03:00
2010-04-10 19:53:17 +04:00
static bool manager_utmp_good ( Manager * m ) {
int r ;
assert ( m ) ;
if ( ( r = mount_path_is_mounted ( m , _PATH_UTMPX ) ) < = 0 ) {
if ( r < 0 )
log_warning ( " Failed to determine whether " _PATH_UTMPX " is mounted: %s " , strerror ( - r ) ) ;
return false ;
}
return true ;
}
void manager_write_utmp_reboot ( Manager * m ) {
int r ;
assert ( m ) ;
if ( m - > utmp_reboot_written )
return ;
if ( m - > running_as ! = MANAGER_INIT )
return ;
if ( ! manager_utmp_good ( m ) )
return ;
if ( ( r = utmp_put_reboot ( m - > boot_timestamp ) ) < 0 ) {
if ( r ! = - ENOENT & & r ! = - EROFS )
log_warning ( " Failed to write utmp/wtmp: %s " , strerror ( - r ) ) ;
return ;
}
m - > utmp_reboot_written = true ;
}
void manager_write_utmp_runlevel ( Manager * m , Unit * u ) {
int runlevel , r ;
assert ( m ) ;
assert ( u ) ;
if ( u - > meta . type ! = UNIT_TARGET )
return ;
if ( m - > running_as ! = MANAGER_INIT )
return ;
if ( ! manager_utmp_good ( m ) )
return ;
if ( ( runlevel = target_get_runlevel ( TARGET ( u ) ) ) < = 0 )
return ;
if ( ( r = utmp_put_runlevel ( 0 , runlevel , 0 ) ) < 0 ) {
if ( r ! = - ENOENT & & r ! = - EROFS )
log_warning ( " Failed to write utmp/wtmp: %s " , strerror ( - r ) ) ;
}
}
2010-04-16 01:16:16 +04:00
void manager_dispatch_bus_name_owner_changed (
Manager * m ,
const char * name ,
const char * old_owner ,
const char * new_owner ) {
Unit * u ;
assert ( m ) ;
assert ( name ) ;
if ( ! ( u = hashmap_get ( m - > watch_bus , name ) ) )
return ;
UNIT_VTABLE ( u ) - > bus_name_owner_change ( u , name , old_owner , new_owner ) ;
}
void manager_dispatch_bus_query_pid_done (
Manager * m ,
const char * name ,
pid_t pid ) {
Unit * u ;
assert ( m ) ;
assert ( name ) ;
assert ( pid > = 1 ) ;
if ( ! ( u = hashmap_get ( m - > watch_bus , name ) ) )
return ;
UNIT_VTABLE ( u ) - > bus_query_pid_done ( u , name , pid ) ;
}
2010-04-21 05:27:44 +04:00
int manager_open_serialization ( FILE * * _f ) {
char * path ;
mode_t saved_umask ;
int fd ;
FILE * f ;
assert ( _f ) ;
if ( asprintf ( & path , " /dev/shm/systemd-%u.dump-XXXXXX " , ( unsigned ) getpid ( ) ) < 0 )
return - ENOMEM ;
saved_umask = umask ( 0077 ) ;
fd = mkostemp ( path , O_RDWR | O_CLOEXEC ) ;
umask ( saved_umask ) ;
if ( fd < 0 ) {
free ( path ) ;
return - errno ;
}
unlink ( path ) ;
log_debug ( " Serializing state to %s " , path ) ;
free ( path ) ;
if ( ! ( f = fdopen ( fd , " w+ " ) ) < 0 )
return - errno ;
* _f = f ;
return 0 ;
}
int manager_serialize ( Manager * m , FILE * f , FDSet * fds ) {
Iterator i ;
Unit * u ;
const char * t ;
int r ;
assert ( m ) ;
assert ( f ) ;
assert ( fds ) ;
HASHMAP_FOREACH_KEY ( u , t , m - > units , i ) {
if ( u - > meta . id ! = t )
continue ;
if ( ! unit_can_serialize ( u ) )
continue ;
/* Start marker */
fputs ( u - > meta . id , f ) ;
fputc ( ' \n ' , f ) ;
if ( ( r = unit_serialize ( u , f , fds ) ) < 0 )
return r ;
}
if ( ferror ( f ) )
return - EIO ;
return 0 ;
}
int manager_deserialize ( Manager * m , FILE * f , FDSet * fds ) {
int r = 0 ;
assert ( m ) ;
assert ( f ) ;
log_debug ( " Deserializing state... " ) ;
for ( ; ; ) {
Unit * u ;
char name [ UNIT_NAME_MAX + 2 ] ;
/* Start marker */
if ( ! fgets ( name , sizeof ( name ) , f ) ) {
if ( feof ( f ) )
break ;
return - errno ;
}
char_array_0 ( name ) ;
if ( ( r = manager_load_unit ( m , strstrip ( name ) , NULL , & u ) ) < 0 )
return r ;
if ( ( r = unit_deserialize ( u , f , fds ) ) < 0 )
return r ;
}
if ( ferror ( f ) )
return - EIO ;
return 0 ;
}
int manager_reload ( Manager * m ) {
int r , q ;
FILE * f ;
FDSet * fds ;
assert ( m ) ;
if ( ( r = manager_open_serialization ( & f ) ) < 0 )
return r ;
if ( ! ( fds = fdset_new ( ) ) ) {
r = - ENOMEM ;
goto finish ;
}
if ( ( r = manager_serialize ( m , f , fds ) ) < 0 )
goto finish ;
if ( fseeko ( f , 0 , SEEK_SET ) < 0 ) {
r = - errno ;
goto finish ;
}
/* From here on there is no way back. */
manager_clear_jobs_and_units ( m ) ;
/* First, enumerate what we can from all config files */
if ( ( q = manager_enumerate ( m ) ) < 0 )
r = q ;
/* Second, deserialize our stored data */
if ( ( q = manager_deserialize ( m , f , fds ) ) < 0 )
r = q ;
fclose ( f ) ;
f = NULL ;
/* Third, fire things up! */
if ( ( q = manager_coldplug ( m ) ) < 0 )
r = q ;
finish :
if ( f )
fclose ( f ) ;
if ( fds )
fdset_free ( fds ) ;
return r ;
}
2010-02-12 23:57:39 +03:00
static const char * const manager_running_as_table [ _MANAGER_RUNNING_AS_MAX ] = {
[ MANAGER_INIT ] = " init " ,
[ MANAGER_SYSTEM ] = " system " ,
2010-02-13 03:07:02 +03:00
[ MANAGER_SESSION ] = " session "
2010-02-12 23:57:39 +03:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( manager_running_as , ManagerRunningAs ) ;