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>
# include <sys/poll.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"
2009-11-18 02:42:52 +03:00
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 ;
2009-11-18 02:42:52 +03:00
2010-01-28 04:01:15 +03:00
assert ( m ) ;
assert_se ( reset_all_signal_handlers ( ) = = 0 ) ;
assert_se ( sigemptyset ( & mask ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGCHLD ) = = 0 ) ;
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 , SIGTERM ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGHUP ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGUSR1 ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGUSR2 ) = = 0 ) ;
assert_se ( sigaddset ( & mask , SIGPIPE ) = = 0 ) ;
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 ;
return 0 ;
}
Manager * manager_new ( void ) {
Manager * m ;
2009-11-18 02:42:52 +03:00
if ( ! ( m = new0 ( Manager , 1 ) ) )
return NULL ;
2010-02-12 23:57:39 +03:00
if ( getpid ( ) = = 1 )
m - > running_as = MANAGER_INIT ;
else if ( getuid ( ) = = 0 )
m - > running_as = MANAGER_SYSTEM ;
else
m - > running_as = MANAGER_USER ;
log_debug ( " systemd running in %s mode. " , manager_running_as_to_string ( m - > running_as ) ) ;
2010-01-29 08:45:59 +03:00
m - > signal_watch . fd = m - > mount_watch . fd = m - > udev_watch . fd = m - > epoll_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 ;
if ( ( m - > epoll_fd = epoll_create1 ( EPOLL_CLOEXEC ) ) < 0 )
goto fail ;
2010-01-28 04:01:15 +03:00
if ( manager_setup_signals ( m ) < 0 )
2010-01-24 02:39:29 +03:00
goto fail ;
2010-02-01 05:33:24 +03:00
/* FIXME: this should be called only when the D-Bus bus daemon is running */
if ( bus_init ( m ) < 0 )
goto fail ;
2009-11-18 02:42:52 +03:00
return m ;
fail :
manager_free ( m ) ;
return NULL ;
}
void manager_free ( Manager * m ) {
2010-01-28 08:45:44 +03:00
UnitType c ;
2010-01-26 23:39:06 +03:00
Unit * u ;
2010-01-20 04:12:51 +03:00
Job * j ;
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-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-02-01 05:33:24 +03:00
bus_done ( m ) ;
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 ) ;
if ( m - > epoll_fd > = 0 )
close_nointr ( m - > epoll_fd ) ;
2010-01-27 06:31:52 +03:00
if ( m - > signal_watch . fd > = 0 )
close_nointr ( m - > signal_watch . fd ) ;
2009-11-18 02:42:52 +03:00
free ( m ) ;
}
2010-01-29 05:18:09 +03:00
int manager_coldplug ( Manager * m ) {
int r ;
UnitType c ;
Iterator i ;
Unit * u ;
char * k ;
assert ( m ) ;
/* First, let's ask every type to load all units from
* disk / kernel that it might know */
for ( c = 0 ; c < _UNIT_TYPE_MAX ; c + + )
if ( unit_vtable [ c ] - > enumerate )
if ( ( r = unit_vtable [ c ] - > enumerate ( m ) ) < 0 )
return r ;
manager_dispatch_load_queue ( m ) ;
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY ( u , k , m - > units , i ) {
/* ignore aliases */
if ( unit_id ( u ) ! = k )
continue ;
if ( UNIT_VTABLE ( u ) - > coldplug )
if ( ( r = UNIT_VTABLE ( u ) - > coldplug ( u ) ) < 0 )
return r ;
}
return 0 ;
}
2010-01-20 22:47:49 +03:00
static void transaction_delete_job ( Manager * m , Job * j ) {
assert ( m ) ;
assert ( j ) ;
2010-01-21 02:51:37 +03:00
/* Deletes one job from the transaction */
2010-01-20 22:47:49 +03:00
manager_transaction_unlink_job ( m , j ) ;
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-01-21 02:51:37 +03:00
transaction_delete_job ( m , j ) ;
}
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-01-20 22:47:49 +03:00
transaction_delete_job ( m , j ) ;
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-01-23 03:52:57 +03:00
j - > forced = j - > forced | | other - > forced ;
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-01-20 22:47:49 +03:00
transaction_delete_job ( m , other ) ;
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-01-26 23:39:06 +03:00
log_debug ( " Try to fix job merging by deleting job %s/%s " , unit_id ( d - > unit ) , job_type_to_string ( d - > type ) ) ;
2010-01-21 02:51:37 +03:00
transaction_delete_job ( m , d ) ;
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-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-01-29 06:11:36 +03:00
log_debug ( " Found cycle on %s/%s " , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ) ;
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-01-29 06:11:36 +03:00
log_debug ( " Walked on cycle path to %s/%s " , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ) ;
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-01-26 23:39:06 +03:00
log_debug ( " Breaking order cycle by deleting job %s/%s " , unit_id ( k - > unit ) , job_type_to_string ( k - > type ) ) ;
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-01-26 23:39:06 +03:00
log_debug ( " Garbage collecting job %s/%s " , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ) ;
2010-01-20 22:47:49 +03:00
transaction_delete_job ( m , j ) ;
2010-01-20 04:12:51 +03:00
again = true ;
break ;
}
} while ( again ) ;
}
static int transaction_is_destructive ( Manager * m , JobMode mode ) {
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 )
log_debug ( " %s/%s would stop a running service. " , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ) ;
if ( changes_existing_job )
log_debug ( " %s/%s would change existing job. " , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ) ;
2010-01-21 04:59:12 +03:00
/* Ok, let's get rid of this */
2010-01-29 06:26:30 +03:00
log_debug ( " Deleting %s/%s to minimize impact. " , unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ) ;
2010-01-21 04:59:12 +03:00
transaction_delete_job ( m , j ) ;
again = true ;
break ;
}
if ( again )
break ;
}
} while ( again ) ;
}
2010-01-20 04:12:51 +03:00
static int transaction_apply ( Manager * m , JobMode mode ) {
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-01-21 02:51:37 +03:00
for ( ; ; ) {
2010-01-21 04:59:12 +03:00
/* Third 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-01-21 04:59:12 +03:00
/* Fourth 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-01-23 03:52:57 +03:00
/* Fifth 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-01-21 04:59:12 +03:00
/* Sixth 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-01-21 04:59:12 +03:00
/* Seventh step: check whether we can actually apply this */
2010-01-20 04:12:51 +03:00
if ( mode = = JOB_FAIL )
2010-01-29 06:11:36 +03:00
if ( ( r = transaction_is_destructive ( m , mode ) ) < 0 ) {
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-01-21 04:59:12 +03:00
/* Eights step: apply changes */
2010-01-29 06:11:36 +03:00
if ( ( r = transaction_apply ( m , mode ) ) < 0 ) {
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-01-26 23:39:06 +03:00
static Job * transaction_add_one_job ( Manager * m , JobType type , Unit * unit , bool force , 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-01-23 03:52:57 +03:00
j - > forced = force ;
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-01-20 04:12:51 +03:00
return j ;
}
2010-01-19 06:15:20 +03:00
2010-01-20 22:47:49 +03:00
void manager_transaction_unlink_job ( Manager * m , Job * j ) {
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
if ( other ) {
2010-01-23 03:52:57 +03:00
log_debug ( " Deleting job %s/%s as dependency of job %s/%s " ,
2010-01-26 23:39:06 +03:00
unit_id ( other - > unit ) , job_type_to_string ( other - > type ) ,
unit_id ( j - > unit ) , job_type_to_string ( j - > type ) ) ;
2010-01-20 22:47:49 +03:00
transaction_delete_job ( m , other ) ;
2010-01-20 21:20:15 +03:00
}
}
2010-01-20 04:12:51 +03:00
}
2010-01-26 23:39:06 +03:00
static int transaction_add_job_and_dependencies ( Manager * m , JobType type , Unit * unit , Job * by , bool matters , bool force , 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-01-26 23:39:06 +03:00
if ( ! ( ret = transaction_add_one_job ( m , type , unit , force , & 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-01-23 05:35:54 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , true , force , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_SOFT_REQUIRES ] , i )
2010-01-23 05:35:54 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , ! force , force , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_WANTS ] , i )
2010-01-23 05:35:54 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , false , force , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUISITE ] , i )
2010-01-23 05:35:54 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_VERIFY_ACTIVE , dep , ret , true , force , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_SOFT_REQUISITE ] , i )
2010-01-23 05:35:54 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_VERIFY_ACTIVE , dep , ret , ! force , force , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 04:12:51 +03:00
goto fail ;
2010-01-26 23:39:06 +03:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_CONFLICTS ] , i )
2010-01-23 05:35:54 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_STOP , dep , ret , true , force , 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-01-23 05:35:54 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , type , dep , ret , true , force , 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-01-26 23:39:06 +03:00
int manager_add_job ( Manager * m , JobType type , Unit * unit , JobMode mode , bool force , 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-01-29 06:11:36 +03:00
log_debug ( " Trying to enqueue job %s/%s " , unit_id ( unit ) , job_type_to_string ( type ) ) ;
2010-02-12 04:21:08 +03:00
if ( ( r = transaction_add_job_and_dependencies ( m , type , unit , NULL , true , force , & 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-01-20 04:12:51 +03:00
if ( ( r = transaction_activate ( m , mode ) ) < 0 )
return r ;
2010-02-12 04:21:08 +03:00
log_debug ( " Enqueued job %s/%s as %u " , unit_id ( unit ) , 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
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-01-27 02:15:56 +03:00
int manager_load_unit ( Manager * m , 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 ;
2010-01-27 02:15:56 +03:00
const char * name ;
2009-11-18 02:42:52 +03:00
assert ( m ) ;
2010-01-27 02:15:56 +03:00
assert ( path ) ;
2009-11-18 02:42:52 +03:00
assert ( _ret ) ;
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 . */
name = file_name_from_path ( path ) ;
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-01-27 02:15:56 +03:00
if ( is_path ( path ) ) {
if ( ! ( ret - > meta . load_path = strdup ( path ) ) ) {
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
* _ret = ret ;
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 )
if ( unit_id ( u ) = = t )
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 ) ) {
Unit * u = ( Unit * ) meta ;
assert ( u - > meta . in_dbus_queue ) ;
bus_unit_send_change_signal ( u ) ;
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 ) ;
2010-01-27 06:31:52 +03:00
log_debug ( " dispatching SIGCHLD " ) ;
2010-01-24 02:39:29 +03:00
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-01-27 06:31:52 +03:00
if ( waitid ( P_ALL , 0 , & si , WEXITED | WNOHANG ) < 0 ) {
if ( errno = = ECHILD )
break ;
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
if ( si . si_pid = = 0 )
break ;
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-01-30 03:55:42 +03:00
log_debug ( " child %llu died (code=%s, status=%i) " , ( long long unsigned ) si . si_pid , sigchld_code_to_string ( si . si_code ) , 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-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-01-27 06:36:30 +03:00
static int manager_process_signal_fd ( Manager * m , bool * quit ) {
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 ) {
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-01-27 06:36:30 +03:00
* quit = true ;
return 0 ;
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-01-27 06:36:30 +03:00
static int process_event ( Manager * m , struct epoll_event * ev , bool * quit ) {
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-01-27 06:36:30 +03:00
if ( ( r = manager_process_signal_fd ( m , quit ) ) < 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-01-27 06:36:30 +03:00
bool quit = false ;
2010-01-24 02:39:29 +03:00
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 ) ;
for ( ; ; ) {
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-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-01-28 00:40:10 +03:00
if ( ( r = process_event ( m , & event , & quit ) ) < 0 )
return r ;
if ( quit )
return 0 ;
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
static const char * const manager_running_as_table [ _MANAGER_RUNNING_AS_MAX ] = {
[ MANAGER_INIT ] = " init " ,
[ MANAGER_SYSTEM ] = " system " ,
[ MANAGER_USER ] = " user "
} ;
DEFINE_STRING_TABLE_LOOKUP ( manager_running_as , ManagerRunningAs ) ;