2009-11-18 00:42:52 +01:00
/*-*- Mode: C; c-basic-offset: 8 -*-*/
2010-02-03 13:03:47 +01:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
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 00:42:52 +01:00
# include <assert.h>
# include <errno.h>
2010-01-18 23:50:13 +01:00
# include <string.h>
2010-01-24 00:39:29 +01:00
# include <sys/epoll.h>
# include <signal.h>
# include <sys/signalfd.h>
# include <sys/wait.h>
# include <unistd.h>
# include <sys/poll.h>
2010-02-14 22:39:40 +01:00
# include <sys/reboot.h>
# include <sys/ioctl.h>
# include <linux/kd.h>
2010-04-13 02:06:27 +02:00
# include <termios.h>
# include <fcntl.h>
2010-04-21 03:27:44 +02:00
# include <sys/types.h>
# include <sys/stat.h>
2010-07-11 00:52:00 +02:00
# include <dirent.h>
2010-08-11 15:19:50 +02:00
# ifdef HAVE_AUDIT
2010-08-11 01:43:23 +02:00
# include <libaudit.h>
2010-08-11 15:19:50 +02:00
# endif
2009-11-18 00:42:52 +01:00
# include "manager.h"
# include "hashmap.h"
# include "macro.h"
# include "strv.h"
2010-01-20 19:19:53 +01:00
# include "log.h"
2010-01-27 06:19:28 +01:00
# include "util.h"
2010-02-01 03:33:24 +01:00
# include "ratelimit.h"
2010-03-31 16:29:55 +02:00
# include "cgroup.h"
# include "mount-setup.h"
2010-04-15 03:11:11 +02:00
# include "unit-name.h"
2010-04-18 03:08:16 +02:00
# include "dbus-unit.h"
# include "dbus-job.h"
2010-05-09 23:53:52 +02:00
# include "missing.h"
2010-06-15 14:45:15 +02:00
# include "path-lookup.h"
2010-06-18 04:22:59 +02:00
# include "special.h"
2010-07-08 02:43:18 +02:00
# include "bus-errors.h"
2009-11-18 00:42:52 +01:00
2010-04-21 06:01:13 +02: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 */
2010-05-16 03:57:07 +02:00
# define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
2010-04-21 06:01:13 +02:00
2010-06-16 05:10:31 +02:00
/* Where clients shall send notification messages to */
# define NOTIFY_SOCKET " / org / freedesktop / systemd1 / notify"
static int manager_setup_notify ( Manager * m ) {
union {
struct sockaddr sa ;
struct sockaddr_un un ;
} sa ;
struct epoll_event ev ;
int one = 1 ;
assert ( m ) ;
m - > notify_watch . type = WATCH_NOTIFY ;
if ( ( m - > notify_watch . fd = socket ( AF_UNIX , SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK , 0 ) ) < 0 ) {
log_error ( " Failed to allocate notification socket: %m " ) ;
return - errno ;
}
zero ( sa ) ;
sa . sa . sa_family = AF_UNIX ;
2010-07-09 23:18:50 +02:00
if ( getpid ( ) ! = 1 )
2010-06-16 05:10:31 +02:00
snprintf ( sa . un . sun_path + 1 , sizeof ( sa . un . sun_path ) - 1 , NOTIFY_SOCKET " /%llu " , random_ull ( ) ) ;
else
strncpy ( sa . un . sun_path + 1 , NOTIFY_SOCKET , sizeof ( sa . un . sun_path ) - 1 ) ;
2010-07-11 02:23:11 +02:00
if ( bind ( m - > notify_watch . fd , & sa . sa , sizeof ( sa_family_t ) + 1 + strlen ( sa . un . sun_path + 1 ) ) < 0 ) {
2010-06-16 05:10:31 +02:00
log_error ( " bind() failed: %m " ) ;
return - errno ;
}
if ( setsockopt ( m - > notify_watch . fd , SOL_SOCKET , SO_PASSCRED , & one , sizeof ( one ) ) < 0 ) {
log_error ( " SO_PASSCRED failed: %m " ) ;
return - errno ;
}
zero ( ev ) ;
ev . events = EPOLLIN ;
ev . data . ptr = & m - > notify_watch ;
if ( epoll_ctl ( m - > epoll_fd , EPOLL_CTL_ADD , m - > notify_watch . fd , & ev ) < 0 )
return - errno ;
2010-06-18 23:12:48 +02:00
if ( ! ( m - > notify_socket = strdup ( sa . un . sun_path + 1 ) ) )
2010-06-16 05:10:31 +02:00
return - ENOMEM ;
return 0 ;
}
2010-04-13 02:06:27 +02: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 " ) ;
2010-05-14 04:36:24 +02:00
if ( ( fd = open_terminal ( " /dev/tty0 " , O_RDWR | O_NOCTTY ) ) < 0 )
2010-04-13 02:06:27 +02:00
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 02:01:15 +01:00
static int manager_setup_signals ( Manager * m ) {
2010-01-24 00:39:29 +01:00
sigset_t mask ;
struct epoll_event ev ;
2010-04-13 02:36:56 +02:00
struct sigaction sa ;
2009-11-18 00:42:52 +01:00
2010-01-28 02:01:15 +01:00
assert ( m ) ;
2010-04-13 02:36:56 +02: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 02:01:15 +01:00
assert_se ( sigemptyset ( & mask ) = = 0 ) ;
2010-06-17 23:22:56 +02:00
sigset_add_many ( & mask ,
SIGCHLD , /* Child died */
SIGTERM , /* Reexecute daemon */
SIGHUP , /* Reload configuration */
SIGUSR1 , /* systemd/upstart: reconnect to D-Bus */
SIGUSR2 , /* systemd: dump status */
SIGINT , /* Kernel sends us this on control-alt-del */
SIGWINCH , /* Kernel sends us this on kbrequest (alt-arrowup) */
SIGPWR , /* Some kernel drivers and upsd send us this on power failure */
SIGRTMIN + 0 , /* systemd: start default.target */
SIGRTMIN + 1 , /* systemd: start rescue.target */
SIGRTMIN + 2 , /* systemd: isolate emergency.target */
SIGRTMIN + 3 , /* systemd: start halt.target */
SIGRTMIN + 4 , /* systemd: start poweroff.target */
SIGRTMIN + 5 , /* systemd: start reboot.target */
- 1 ) ;
2010-01-28 02:01:15 +01:00
assert_se ( sigprocmask ( SIG_SETMASK , & mask , NULL ) = = 0 ) ;
2010-01-29 06:04:08 +01:00
m - > signal_watch . type = WATCH_SIGNAL ;
2010-01-28 02:01:15 +01: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-06-19 03:15:59 +02:00
if ( m - > running_as = = MANAGER_SYSTEM )
2010-04-13 02:06:27 +02:00
return enable_special_signals ( m ) ;
2010-02-14 22:39:40 +01:00
2010-01-28 02:01:15 +01:00
return 0 ;
}
2010-07-07 00:00:59 +02:00
int manager_new ( ManagerRunningAs running_as , Manager * * _m ) {
2010-01-28 02:01:15 +01:00
Manager * m ;
2010-03-31 16:29:55 +02:00
int r = - ENOMEM ;
assert ( _m ) ;
2010-04-06 23:55:42 +02:00
assert ( running_as > = 0 ) ;
assert ( running_as < _MANAGER_RUNNING_AS_MAX ) ;
2010-01-28 02:01:15 +01:00
2009-11-18 00:42:52 +01:00
if ( ! ( m = new0 ( Manager , 1 ) ) )
2010-03-31 16:29:55 +02:00
return - ENOMEM ;
2009-11-18 00:42:52 +01:00
2010-07-01 00:26:44 +02:00
dual_timestamp_get ( & m - > startup_timestamp ) ;
2010-04-10 17:53:17 +02:00
2010-04-06 23:55:42 +02:00
m - > running_as = running_as ;
2010-07-05 00:58:07 +02:00
m - > name_data_slot = m - > subscribed_data_slot = - 1 ;
2010-04-21 03:27:44 +02:00
m - > exit_code = _MANAGER_EXIT_CODE_INVALID ;
2010-06-18 20:15:34 +02:00
m - > pin_cgroupfs_fd = - 1 ;
2010-04-13 02:06:27 +02:00
2010-08-11 01:43:23 +02:00
# ifdef HAVE_AUDIT
m - > audit_fd = - 1 ;
# endif
2010-04-16 23:24:39 +02:00
m - > signal_watch . fd = m - > mount_watch . fd = m - > udev_watch . fd = m - > epoll_fd = m - > dev_autofs_fd = - 1 ;
2010-02-01 03:33:24 +01:00
m - > current_job_id = 1 ; /* start as id #1, so that we can leave #0 around as "null-like" value */
2010-01-24 00:39:29 +01:00
2010-05-09 23:53:52 +02:00
if ( ! ( m - > environment = strv_copy ( environ ) ) )
goto fail ;
2010-01-26 21:39:06 +01:00
if ( ! ( m - > units = hashmap_new ( string_hash_func , string_compare_func ) ) )
2009-11-18 00:42:52 +01:00
goto fail ;
if ( ! ( m - > jobs = hashmap_new ( trivial_hash_func , trivial_compare_func ) ) )
goto fail ;
2010-01-20 02:12:51 +01:00
if ( ! ( m - > transaction_jobs = hashmap_new ( trivial_hash_func , trivial_compare_func ) ) )
2009-11-18 00:42:52 +01:00
goto fail ;
2010-01-24 00:39:29 +01:00
if ( ! ( m - > watch_pids = hashmap_new ( trivial_hash_func , trivial_compare_func ) ) )
goto fail ;
2010-03-31 16:29:55 +02:00
if ( ! ( m - > cgroup_bondings = hashmap_new ( string_hash_func , string_compare_func ) ) )
goto fail ;
2010-04-15 23:16:16 +02:00
if ( ! ( m - > watch_bus = hashmap_new ( string_hash_func , string_compare_func ) ) )
goto fail ;
2010-01-24 00:39:29 +01:00
if ( ( m - > epoll_fd = epoll_create1 ( EPOLL_CLOEXEC ) ) < 0 )
goto fail ;
2010-06-15 14:45:15 +02:00
if ( ( r = lookup_paths_init ( & m - > lookup_paths , m - > running_as ) ) < 0 )
2010-02-14 22:39:40 +01:00
goto fail ;
2010-03-31 16:29:55 +02:00
if ( ( r = manager_setup_signals ( m ) ) < 0 )
goto fail ;
if ( ( r = manager_setup_cgroup ( m ) ) < 0 )
2010-01-24 00:39:29 +01:00
goto fail ;
2010-06-16 05:10:31 +02:00
if ( ( r = manager_setup_notify ( m ) ) < 0 )
goto fail ;
2010-04-06 16:32:07 +02:00
/* Try to connect to the busses, if possible. */
2010-06-19 03:04:04 +02:00
if ( ( r = bus_init ( m ) ) < 0 )
2010-02-01 03:33:24 +01:00
goto fail ;
2010-08-11 17:52:01 +02:00
# ifdef HAVE_AUDIT
2010-08-11 01:43:23 +02:00
if ( ( m - > audit_fd = audit_open ( ) ) < 0 )
log_error ( " Failed to connect to audit log: %m " ) ;
2010-08-11 17:52:01 +02:00
# endif
2010-08-11 01:43:23 +02:00
2010-03-31 16:29:55 +02:00
* _m = m ;
return 0 ;
2009-11-18 00:42:52 +01:00
fail :
manager_free ( m ) ;
2010-03-31 16:29:55 +02:00
return r ;
2009-11-18 00:42:52 +01:00
}
2010-04-06 02:43:58 +02: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 ) ;
2010-06-19 16:57:29 +02:00
unit_free ( ( Unit * ) meta ) ;
2010-04-06 02:43:58 +02:00
n + + ;
}
return n ;
}
2010-04-23 18:47:49 +02:00
enum {
GC_OFFSET_IN_PATH , /* This one is on the path we were travelling */
GC_OFFSET_UNSURE , /* No clue */
GC_OFFSET_GOOD , /* We still need this unit */
GC_OFFSET_BAD , /* We don't need this unit anymore */
_GC_OFFSET_MAX
} ;
static void unit_gc_sweep ( Unit * u , unsigned gc_marker ) {
2010-04-21 06:01:13 +02:00
Iterator i ;
Unit * other ;
2010-04-23 18:47:49 +02:00
bool is_bad ;
2010-04-21 06:01:13 +02:00
assert ( u ) ;
2010-04-23 18:47:49 +02:00
if ( u - > meta . gc_marker = = gc_marker + GC_OFFSET_GOOD | |
u - > meta . gc_marker = = gc_marker + GC_OFFSET_BAD | |
u - > meta . gc_marker = = gc_marker + GC_OFFSET_IN_PATH )
2010-04-21 06:01:13 +02:00
return ;
2010-04-22 02:41:14 +02:00
if ( u - > meta . in_cleanup_queue )
2010-04-21 06:01:13 +02:00
goto bad ;
if ( unit_check_gc ( u ) )
goto good ;
2010-04-23 18:47:49 +02:00
u - > meta . gc_marker = gc_marker + GC_OFFSET_IN_PATH ;
is_bad = true ;
2010-04-21 06:01:13 +02:00
SET_FOREACH ( other , u - > meta . dependencies [ UNIT_REFERENCED_BY ] , i ) {
unit_gc_sweep ( other , gc_marker ) ;
2010-04-23 18:47:49 +02:00
if ( other - > meta . gc_marker = = gc_marker + GC_OFFSET_GOOD )
2010-04-21 06:01:13 +02:00
goto good ;
2010-04-23 18:47:49 +02:00
if ( other - > meta . gc_marker ! = gc_marker + GC_OFFSET_BAD )
is_bad = false ;
2010-04-21 06:01:13 +02:00
}
2010-04-23 18:47:49 +02:00
if ( is_bad )
goto bad ;
/* We were unable to find anything out about this entry, so
* let ' s investigate it later */
u - > meta . gc_marker = gc_marker + GC_OFFSET_UNSURE ;
unit_add_to_gc_queue ( u ) ;
return ;
2010-04-21 06:01:13 +02:00
bad :
2010-04-23 18:47:49 +02:00
/* We definitely know that this one is not useful anymore, so
* let ' s mark it for deletion */
u - > meta . gc_marker = gc_marker + GC_OFFSET_BAD ;
unit_add_to_cleanup_queue ( u ) ;
2010-04-21 06:01:13 +02:00
return ;
good :
2010-04-23 18:47:49 +02:00
u - > meta . gc_marker = gc_marker + GC_OFFSET_GOOD ;
2010-04-21 06:01:13 +02:00
}
static unsigned manager_dispatch_gc_queue ( Manager * m ) {
Meta * meta ;
unsigned n = 0 ;
2010-04-23 18:47:49 +02:00
unsigned gc_marker ;
2010-04-21 06:01:13 +02:00
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-23 18:47:49 +02:00
m - > gc_marker + = _GC_OFFSET_MAX ;
if ( m - > gc_marker + _GC_OFFSET_MAX < = _GC_OFFSET_MAX )
2010-04-22 02:41:14 +02:00
m - > gc_marker = 1 ;
2010-04-21 06:01:13 +02:00
2010-04-23 18:47:49 +02:00
gc_marker = m - > gc_marker ;
2010-04-21 06:01:13 +02:00
while ( ( meta = m - > gc_queue ) ) {
assert ( meta - > in_gc_queue ) ;
2010-06-19 16:57:29 +02:00
unit_gc_sweep ( ( Unit * ) meta , gc_marker ) ;
2010-04-23 18:47:49 +02:00
2010-04-21 06:01:13 +02:00
LIST_REMOVE ( Meta , gc_queue , m - > gc_queue , meta ) ;
meta - > in_gc_queue = false ;
n + + ;
2010-04-23 18:47:49 +02:00
if ( meta - > gc_marker = = gc_marker + GC_OFFSET_BAD | |
meta - > gc_marker = = gc_marker + GC_OFFSET_UNSURE ) {
2010-04-21 06:01:13 +02:00
log_debug ( " Collecting %s " , meta - > id ) ;
2010-04-23 18:47:49 +02:00
meta - > gc_marker = gc_marker + GC_OFFSET_BAD ;
2010-06-19 16:57:29 +02:00
unit_add_to_cleanup_queue ( ( Unit * ) meta ) ;
2010-04-21 06:01:13 +02:00
}
}
m - > n_in_gc_queue = 0 ;
m - > gc_queue_timestamp = 0 ;
return n ;
}
2010-04-21 03:27:44 +02:00
static void manager_clear_jobs_and_units ( Manager * m ) {
2010-01-20 02:12:51 +01:00
Job * j ;
2010-04-21 03:27:44 +02:00
Unit * u ;
2009-11-18 00:42:52 +01:00
assert ( m ) ;
2010-01-26 21:39:06 +01:00
while ( ( j = hashmap_first ( m - > transaction_jobs ) ) )
2010-01-20 02:12:51 +01:00
job_free ( j ) ;
2010-01-26 21:39:06 +01:00
while ( ( u = hashmap_first ( m - > units ) ) )
unit_free ( u ) ;
2010-06-05 02:16:20 +02:00
manager_dispatch_cleanup_queue ( m ) ;
assert ( ! m - > load_queue ) ;
assert ( ! m - > run_queue ) ;
assert ( ! m - > dbus_unit_queue ) ;
assert ( ! m - > dbus_job_queue ) ;
assert ( ! m - > cleanup_queue ) ;
assert ( ! m - > gc_queue ) ;
assert ( hashmap_isempty ( m - > transaction_jobs ) ) ;
assert ( hashmap_isempty ( m - > jobs ) ) ;
assert ( hashmap_isempty ( m - > units ) ) ;
2010-04-21 03:27:44 +02:00
}
void manager_free ( Manager * m ) {
UnitType c ;
2010-01-26 21:39:06 +01:00
2010-04-21 03:27:44 +02:00
assert ( m ) ;
manager_clear_jobs_and_units ( m ) ;
2010-04-06 02:43:58 +02:00
2010-01-28 06:45:44 +01:00
for ( c = 0 ; c < _UNIT_TYPE_MAX ; c + + )
if ( unit_vtable [ c ] - > shutdown )
unit_vtable [ c ] - > shutdown ( m ) ;
2010-04-21 03:27:44 +02:00
/* If we reexecute ourselves, we keep the root cgroup
* around */
2010-07-11 00:50:49 +02:00
manager_shutdown_cgroup ( m , m - > exit_code ! = MANAGER_REEXECUTE ) ;
2010-03-31 16:29:55 +02:00
2010-06-19 03:04:04 +02:00
bus_done ( m ) ;
2010-02-01 03:33:24 +01:00
2010-01-26 21:39:06 +01:00
hashmap_free ( m - > units ) ;
2009-11-18 00:42:52 +01:00
hashmap_free ( m - > jobs ) ;
2010-01-20 02:12:51 +01:00
hashmap_free ( m - > transaction_jobs ) ;
2010-01-24 00:39:29 +01:00
hashmap_free ( m - > watch_pids ) ;
2010-04-15 23:16:16 +02:00
hashmap_free ( m - > watch_bus ) ;
2010-01-24 00:39:29 +01:00
if ( m - > epoll_fd > = 0 )
2010-04-21 03:27:44 +02:00
close_nointr_nofail ( m - > epoll_fd ) ;
2010-01-27 04:31:52 +01:00
if ( m - > signal_watch . fd > = 0 )
2010-04-21 03:27:44 +02:00
close_nointr_nofail ( m - > signal_watch . fd ) ;
2010-06-16 05:10:31 +02:00
if ( m - > notify_watch . fd > = 0 )
close_nointr_nofail ( m - > notify_watch . fd ) ;
2009-11-18 00:42:52 +01:00
2010-08-11 01:43:23 +02:00
# ifdef HAVE_AUDIT
if ( m - > audit_fd > = 0 )
audit_close ( m - > audit_fd ) ;
# endif
2010-06-18 23:12:48 +02:00
free ( m - > notify_socket ) ;
2010-06-15 14:45:15 +02:00
lookup_paths_free ( & m - > lookup_paths ) ;
2010-05-09 23:53:52 +02:00
strv_free ( m - > environment ) ;
2010-02-13 01:07:02 +01:00
2010-03-31 16:29:55 +02:00
hashmap_free ( m - > cgroup_bondings ) ;
2010-07-11 00:50:49 +02:00
set_free_free ( m - > unit_path_cache ) ;
2010-06-18 20:15:34 +02:00
2009-11-18 00:42:52 +01:00
free ( m ) ;
}
2010-04-21 03:27:44 +02:00
int manager_enumerate ( Manager * m ) {
int r = 0 , q ;
2010-01-29 03:18:09 +01:00
UnitType c ;
assert ( m ) ;
2010-04-21 03:27:44 +02:00
/* Let's ask every type to load all units from disk/kernel
* that it might know */
2010-01-29 03:18:09 +01:00
for ( c = 0 ; c < _UNIT_TYPE_MAX ; c + + )
if ( unit_vtable [ c ] - > enumerate )
2010-04-21 03:27:44 +02:00
if ( ( q = unit_vtable [ c ] - > enumerate ( m ) ) < 0 )
r = q ;
2010-01-29 03:18:09 +01:00
manager_dispatch_load_queue ( m ) ;
2010-04-21 03:27:44 +02:00
return r ;
}
int manager_coldplug ( Manager * m ) {
int r = 0 , q ;
Iterator i ;
Unit * u ;
char * k ;
assert ( m ) ;
2010-01-29 03:18:09 +01:00
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY ( u , k , m - > units , i ) {
/* ignore aliases */
2010-04-15 03:11:11 +02:00
if ( u - > meta . id ! = k )
2010-01-29 03:18:09 +01:00
continue ;
2010-06-03 14:26:50 +02:00
if ( ( q = unit_coldplug ( u ) ) < 0 )
r = q ;
2010-01-29 03:18:09 +01:00
}
2010-04-21 03:27:44 +02:00
return r ;
}
2010-07-11 00:52:00 +02:00
static void manager_build_unit_path_cache ( Manager * m ) {
char * * i ;
DIR * d = NULL ;
int r ;
assert ( m ) ;
set_free_free ( m - > unit_path_cache ) ;
if ( ! ( m - > unit_path_cache = set_new ( string_hash_func , string_compare_func ) ) ) {
log_error ( " Failed to allocate unit path cache. " ) ;
return ;
}
/* This simply builds a list of files we know exist, so that
* we don ' t always have to go to disk */
STRV_FOREACH ( i , m - > lookup_paths . unit_path ) {
struct dirent * de ;
if ( ! ( d = opendir ( * i ) ) ) {
log_error ( " Failed to open directory: %m " ) ;
continue ;
}
while ( ( de = readdir ( d ) ) ) {
char * p ;
if ( ignore_file ( de - > d_name ) )
continue ;
if ( asprintf ( & p , " %s/%s " , streq ( * i , " / " ) ? " " : * i , de - > d_name ) < 0 ) {
r = - ENOMEM ;
goto fail ;
}
if ( ( r = set_put ( m - > unit_path_cache , p ) ) < 0 ) {
free ( p ) ;
goto fail ;
}
}
closedir ( d ) ;
d = NULL ;
}
return ;
fail :
log_error ( " Failed to build unit path cache: %s " , strerror ( - r ) ) ;
set_free_free ( m - > unit_path_cache ) ;
m - > unit_path_cache = NULL ;
if ( d )
closedir ( d ) ;
}
2010-04-21 03:27:44 +02:00
int manager_startup ( Manager * m , FILE * serialization , FDSet * fds ) {
int r , q ;
assert ( m ) ;
2010-07-11 00:52:00 +02:00
manager_build_unit_path_cache ( m ) ;
2010-07-13 19:01:20 +02:00
/* If we will deserialize make sure that during enumeration
* this is already known , so we increase the counter here
* already */
if ( serialization )
m - > n_deserializing + + ;
2010-04-21 03:27:44 +02:00
/* 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-07-13 19:01:20 +02:00
if ( serialization ) {
assert ( m - > n_deserializing > 0 ) ;
m - > n_deserializing - - ;
}
2010-04-21 03:27:44 +02:00
return r ;
2010-01-29 03:18:09 +01:00
}
2010-04-06 02:43:58 +02:00
static void transaction_delete_job ( Manager * m , Job * j , bool delete_dependencies ) {
2010-01-20 20:47:49 +01:00
assert ( m ) ;
assert ( j ) ;
2010-01-21 00:51:37 +01:00
/* Deletes one job from the transaction */
2010-04-06 02:43:58 +02:00
manager_transaction_unlink_job ( m , j , delete_dependencies ) ;
2010-01-20 20:47:49 +01:00
2010-01-26 19:25:02 +01:00
if ( ! j - > installed )
2010-01-20 20:47:49 +01:00
job_free ( j ) ;
}
2010-01-26 21:39:06 +01:00
static void transaction_delete_unit ( Manager * m , Unit * u ) {
2010-01-21 00:51:37 +01:00
Job * j ;
2010-01-26 21:39:06 +01:00
/* Deletes all jobs associated with a certain unit from the
2010-01-21 00:51:37 +01:00
* transaction */
2010-01-26 21:39:06 +01:00
while ( ( j = hashmap_get ( m - > transaction_jobs , u ) ) )
2010-04-06 02:43:58 +02:00
transaction_delete_job ( m , j , true ) ;
2010-01-21 00:51:37 +01:00
}
2010-01-27 01:43:18 +01: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 04:15:20 +01:00
static void transaction_abort ( Manager * m ) {
Job * j ;
assert ( m ) ;
2010-01-20 02:12:51 +01:00
while ( ( j = hashmap_first ( m - > transaction_jobs ) ) )
2010-01-26 19:25:02 +01:00
if ( j - > installed )
2010-04-06 02:43:58 +02:00
transaction_delete_job ( m , j , true ) ;
2010-01-20 02:12:51 +01:00
else
job_free ( j ) ;
assert ( hashmap_isempty ( m - > transaction_jobs ) ) ;
2010-01-27 01:43:18 +01:00
transaction_clean_dependencies ( m ) ;
2010-01-20 02:12:51 +01:00
}
static void transaction_find_jobs_that_matter_to_anchor ( Manager * m , Job * j , unsigned generation ) {
JobDependency * l ;
assert ( m ) ;
2010-01-26 21:39:06 +01:00
/* A recursive sweep through the graph that marks all units
2010-01-21 00:51:37 +01: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 07:02:51 +01:00
if ( j )
l = j - > subject_list ;
else
l = m - > transaction_anchor ;
LIST_FOREACH ( subject , l , l ) {
2010-01-20 02:12:51 +01:00
/* This link does not matter */
if ( ! l - > matters )
continue ;
2010-01-26 21:39:06 +01:00
/* This unit has already been marked */
2010-01-20 02:12:51 +01: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 05:03:52 +01:00
static void transaction_merge_and_delete_job ( Manager * m , Job * j , Job * other , JobType t ) {
2010-01-20 02:12:51 +01:00
JobDependency * l , * last ;
assert ( j ) ;
assert ( other ) ;
2010-01-26 21:39:06 +01:00
assert ( j - > unit = = other - > unit ) ;
2010-01-26 19:25:02 +01:00
assert ( ! j - > installed ) ;
2010-01-20 02:12:51 +01:00
2010-01-21 00:51:37 +01:00
/* Merges 'other' into 'j' and then deletes j. */
2010-01-20 02:12:51 +01:00
j - > type = t ;
j - > state = JOB_WAITING ;
2010-04-15 03:11:11 +02:00
j - > override = j - > override | | other - > override ;
2010-01-20 02:12:51 +01: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 07:02:51 +01:00
LIST_FOREACH ( subject , l , other - > subject_list ) {
2010-01-20 02:12:51 +01: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 07:02:51 +01:00
LIST_FOREACH ( object , l , other - > object_list ) {
2010-01-20 02:12:51 +01: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 02:43:58 +02:00
transaction_delete_job ( m , other , true ) ;
2010-01-20 02:12:51 +01:00
}
2010-08-09 22:32:30 +02:00
static bool job_is_conflicted_by ( Job * j ) {
JobDependency * l ;
assert ( j ) ;
/* Returns true if this job is pulled in by a least one
* ConflictedBy dependency . */
LIST_FOREACH ( object , l , j - > object_list )
if ( l - > conflicts )
return true ;
return false ;
}
2010-01-20 02:12:51 +01:00
2010-01-23 01:52:57 +01:00
static int delete_one_unmergeable_job ( Manager * m , Job * j ) {
2010-01-21 00:51:37 +01: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 04:18:44 +01:00
LIST_FOREACH ( transaction , j , j )
LIST_FOREACH ( transaction , k , j - > transaction_next ) {
2010-01-21 00:51:37 +01:00
Job * d ;
/* Is this one mergeable? Then skip it */
2010-01-23 01:52:57 +01:00
if ( job_type_is_mergeable ( j - > type , k - > type ) )
2010-01-21 00:51:37 +01:00
continue ;
/* Ok, we found two that conflict, let's see if we can
* drop one of them */
2010-08-09 22:32:30 +02:00
if ( ! j - > matters_to_anchor & & ! k - > matters_to_anchor ) {
/* Both jobs don't matter, so let's
* find the one that is smarter to
* remove . Let ' s think positive and
* rather remove stops then starts - -
* except if something is being
* stopped because it is conflicted by
* another unit in which case we
* rather remove the start . */
log_debug ( " Looking at job %s/%s conflicted_by=%s " , j - > unit - > meta . id , job_type_to_string ( j - > type ) , yes_no ( j - > type = = JOB_STOP & & job_is_conflicted_by ( j ) ) ) ;
log_debug ( " Looking at job %s/%s conflicted_by=%s " , k - > unit - > meta . id , job_type_to_string ( k - > type ) , yes_no ( k - > type = = JOB_STOP & & job_is_conflicted_by ( k ) ) ) ;
if ( j - > type = = JOB_STOP ) {
if ( job_is_conflicted_by ( j ) )
d = k ;
else
d = j ;
} else if ( k - > type = = JOB_STOP ) {
if ( job_is_conflicted_by ( k ) )
d = j ;
else
d = k ;
}
} else if ( ! j - > matters_to_anchor )
2010-01-21 00:51:37 +01:00
d = j ;
else if ( ! k - > matters_to_anchor )
d = k ;
else
return - ENOEXEC ;
/* Ok, we can drop one, so let's do so. */
2010-08-06 02:58:46 +02:00
log_debug ( " Fixing conflicting jobs by deleting job %s/%s " , d - > unit - > meta . id , job_type_to_string ( d - > type ) ) ;
2010-04-06 02:43:58 +02:00
transaction_delete_job ( m , d , true ) ;
2010-01-21 00:51:37 +01:00
return 0 ;
}
return - EINVAL ;
}
2010-07-08 02:43:18 +02:00
static int transaction_merge_jobs ( Manager * m , DBusError * e ) {
2010-01-19 04:15:20 +01:00
Job * j ;
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-20 02:12:51 +01:00
int r ;
assert ( m ) ;
2010-01-21 00:51:37 +01: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 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-21 00:51:37 +01:00
JobType t ;
Job * k ;
t = j - > type ;
2010-01-26 04:18:44 +01:00
LIST_FOREACH ( transaction , k , j - > transaction_next ) {
2010-01-21 00:51:37 +01: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 01:52:57 +01:00
if ( ( r = delete_one_unmergeable_job ( m , j ) ) > = 0 )
2010-01-21 00:51:37 +01: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 */
2010-07-08 02:43:18 +02:00
dbus_set_error ( e , BUS_ERROR_TRANSACTION_JOBS_CONFLICTING , " Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured. " ,
job_type_to_string ( t ) , job_type_to_string ( k - > type ) , k - > unit - > meta . id ) ;
2010-01-21 00:51:37 +01:00
return r ;
}
}
/* Second step, merge the jobs. */
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-20 02:12:51 +01:00
JobType t = j - > type ;
Job * k ;
2010-01-21 02:59:12 +01:00
/* Merge all transactions */
2010-01-26 04:18:44 +01:00
LIST_FOREACH ( transaction , k , j - > transaction_next )
2010-01-21 00:51:37 +01:00
assert_se ( job_type_merge ( & t , k - > type ) = = 0 ) ;
2010-01-20 02:12:51 +01:00
2010-01-23 01:52:57 +01:00
/* If an active job is mergeable, merge it too */
2010-01-26 21:39:06 +01:00
if ( j - > unit - > meta . job )
job_type_merge ( & t , j - > unit - > meta . job - > type ) ; /* Might fail. Which is OK */
2010-01-21 02:59:12 +01:00
2010-01-20 02:12:51 +01:00
while ( ( k = j - > transaction_next ) ) {
2010-01-26 19:25:02 +01:00
if ( j - > installed ) {
2010-01-20 05:03:52 +01:00
transaction_merge_and_delete_job ( m , k , j , t ) ;
2010-01-20 02:12:51 +01:00
j = k ;
} else
2010-01-20 05:03:52 +01:00
transaction_merge_and_delete_job ( m , j , k , t ) ;
2010-01-20 02:12:51 +01:00
}
assert ( ! j - > transaction_next ) ;
assert ( ! j - > transaction_prev ) ;
}
2010-01-20 05:03:52 +01:00
return 0 ;
2010-01-20 02:12:51 +01:00
}
2010-04-06 02:43:58 +02: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 03:11:11 +02:00
log_debug ( " Found redundant job %s/%s, dropping. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-04-06 02:43:58 +02:00
transaction_delete_job ( m , j , false ) ;
again = true ;
break ;
}
} while ( again ) ;
}
2010-01-26 21:39:06 +01:00
static bool unit_matters_to_anchor ( Unit * u , Job * j ) {
assert ( u ) ;
2010-01-21 00:51:37 +01:00
assert ( ! j - > transaction_prev ) ;
2010-01-26 21:39:06 +01:00
/* Checks whether at least one of the jobs for this unit
2010-01-21 00:51:37 +01:00
* matters to the anchor . */
2010-01-26 04:18:44 +01:00
LIST_FOREACH ( transaction , j , j )
2010-01-21 00:51:37 +01:00
if ( j - > matters_to_anchor )
return true ;
return false ;
}
2010-07-08 02:43:18 +02:00
static int transaction_verify_order_one ( Manager * m , Job * j , Job * from , unsigned generation , DBusError * e ) {
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-26 21:39:06 +01:00
Unit * u ;
2010-01-19 04:15:20 +01:00
int r ;
2010-01-20 02:12:51 +01:00
assert ( m ) ;
assert ( j ) ;
2010-01-21 00:51:37 +01: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 02:12:51 +01:00
2010-06-03 03:00:47 +02:00
/* Have we seen this before? */
if ( j - > generation = = generation ) {
2010-08-05 20:39:45 +02:00
Job * k , * delete ;
2010-01-20 02:12:51 +01:00
2010-06-03 03:00:47 +02:00
/* If the marker is NULL we have been here already and
* decided the job was loop - free from here . Hence
* shortcut things and return right - away . */
if ( ! j - > marker )
return 0 ;
2010-01-20 02:12:51 +01:00
2010-06-03 03:00:47 +02:00
/* So, the marker is not NULL and we already have been
* here . We have a 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-07-07 17:57:54 +02:00
log_warning ( " Found ordering cycle on %s/%s " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 04:11:36 +01:00
2010-08-05 20:39:45 +02:00
delete = NULL ;
2010-06-03 03:00:47 +02:00
for ( k = from ; k ; k = ( ( k - > generation = = generation & & k - > marker ! = k ) ? k - > marker : NULL ) ) {
2010-01-21 00:51:37 +01:00
2010-07-07 17:57:54 +02:00
log_info ( " Walked on cycle path to %s/%s " , k - > unit - > meta . id , job_type_to_string ( k - > type ) ) ;
2010-01-29 04:11:36 +01:00
2010-08-05 20:39:45 +02:00
if ( ! delete & &
! k - > installed & &
2010-01-26 21:39:06 +01:00
! unit_matters_to_anchor ( k - > unit , k ) ) {
2010-01-21 00:51:37 +01:00
/* Ok, we can drop this one, so let's
* do so . */
2010-08-05 20:39:45 +02:00
delete = k ;
2010-01-20 02:12:51 +01:00
}
/* Check if this in fact was the beginning of
2010-01-20 05:03:52 +01:00
* the cycle */
2010-01-20 02:12:51 +01:00
if ( k = = j )
break ;
}
2010-08-05 20:39:45 +02:00
if ( delete ) {
log_warning ( " Breaking ordering cycle by deleting job %s/%s " , k - > unit - > meta . id , job_type_to_string ( k - > type ) ) ;
transaction_delete_unit ( m , delete - > unit ) ;
return - EAGAIN ;
}
2010-07-07 17:57:54 +02:00
log_error ( " Unable to break cycle " ) ;
2010-01-29 04:11:36 +01:00
2010-07-08 02:43:18 +02:00
dbus_set_error ( e , BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC , " Transaction order is cyclic. See logs for details. " ) ;
2010-01-21 00:51:37 +01:00
return - ENOEXEC ;
2010-01-20 02:12:51 +01:00
}
2010-01-21 00:51:37 +01:00
/* Make the marker point to where we come from, so that we can
2010-06-03 03:00:47 +02:00
* find our way backwards if we want to break a cycle . We use
* a special marker for the beginning : we point to
* ourselves . */
j - > marker = from ? from : j ;
2010-01-20 02:12:51 +01:00
j - > generation = generation ;
2010-01-21 00:51:37 +01:00
/* We assume that the the dependencies are bidirectional, and
2010-01-26 21:39:06 +01:00
* hence can ignore UNIT_AFTER */
SET_FOREACH ( u , j - > unit - > meta . dependencies [ UNIT_BEFORE ] , i ) {
2010-01-20 02:12:51 +01:00
Job * o ;
2010-01-26 21:39:06 +01:00
/* Is there a job for this unit? */
if ( ! ( o = hashmap_get ( m - > transaction_jobs , u ) ) )
2010-01-21 00:51:37 +01:00
/* Ok, there is no job for this in the
* transaction , but maybe there is already one
* running ? */
2010-01-26 21:39:06 +01:00
if ( ! ( o = u - > meta . job ) )
2010-01-20 02:12:51 +01:00
continue ;
2010-07-08 02:43:18 +02:00
if ( ( r = transaction_verify_order_one ( m , o , j , generation , e ) ) < 0 )
2010-01-20 02:12:51 +01:00
return r ;
}
2010-01-29 04:11:36 +01:00
/* Ok, let's backtrack, and remember that this entry is not on
* our path anymore . */
j - > marker = NULL ;
2010-01-20 02:12:51 +01:00
return 0 ;
}
2010-07-08 02:43:18 +02:00
static int transaction_verify_order ( Manager * m , unsigned * generation , DBusError * e ) {
2010-01-21 00:51:37 +01:00
Job * j ;
int r ;
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-06-03 03:00:47 +02:00
unsigned g ;
2010-01-21 00:51:37 +01:00
2010-01-20 02:12:51 +01:00
assert ( m ) ;
assert ( generation ) ;
2010-01-21 00:51:37 +01: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 02:12:51 +01:00
2010-06-03 03:00:47 +02:00
g = ( * generation ) + + ;
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i )
2010-07-08 02:43:18 +02:00
if ( ( r = transaction_verify_order_one ( m , j , NULL , g , e ) ) < 0 )
2010-01-21 00:51:37 +01:00
return r ;
2010-01-20 02:12:51 +01:00
return 0 ;
}
static void transaction_collect_garbage ( Manager * m ) {
bool again ;
assert ( m ) ;
2010-01-21 00:51:37 +01:00
/* Drop jobs that are not required by any other job */
2010-01-20 02:12:51 +01:00
do {
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-20 02:12:51 +01:00
Job * j ;
again = false ;
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-20 02:12:51 +01:00
if ( j - > object_list )
continue ;
2010-04-15 03:11:11 +02:00
log_debug ( " Garbage collecting job %s/%s " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-04-06 02:43:58 +02:00
transaction_delete_job ( m , j , true ) ;
2010-01-20 02:12:51 +01:00
again = true ;
break ;
}
} while ( again ) ;
}
2010-07-08 02:43:18 +02:00
static int transaction_is_destructive ( Manager * m , DBusError * e ) {
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-20 02:12:51 +01:00
Job * j ;
2010-01-19 04:15:20 +01:00
assert ( m ) ;
2010-01-20 02:12:51 +01:00
/* Checks whether applying this transaction means that
* existing jobs would be replaced */
2010-01-19 04:15:20 +01:00
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-21 02:59:12 +01:00
/* Assume merged */
assert ( ! j - > transaction_prev ) ;
assert ( ! j - > transaction_next ) ;
2010-01-26 21:39:06 +01:00
if ( j - > unit - > meta . job & &
j - > unit - > meta . job ! = j & &
2010-07-08 02:43:18 +02:00
! job_type_is_superset ( j - > type , j - > unit - > meta . job - > type ) ) {
dbus_set_error ( e , BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE , " Transaction is destructive. " ) ;
2010-01-20 02:12:51 +01:00
return - EEXIST ;
2010-07-08 02:43:18 +02:00
}
2010-01-21 02:59:12 +01:00
}
2010-01-19 04:15:20 +01:00
2010-01-20 02:12:51 +01:00
return 0 ;
}
2010-01-21 02:59:12 +01: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 04:18:44 +01:00
Iterator i ;
2010-01-21 02:59:12 +01:00
again = false ;
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
LIST_FOREACH ( transaction , j , j ) {
2010-01-29 04:26:30 +01:00
bool stops_running_service , changes_existing_job ;
2010-01-21 02:59:12 +01: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 04:26:30 +01:00
stops_running_service =
j - > type = = JOB_STOP & & UNIT_IS_ACTIVE_OR_ACTIVATING ( unit_active_state ( j - > unit ) ) ;
changes_existing_job =
2010-08-05 20:49:35 +02:00
j - > unit - > meta . job & & job_type_is_conflicting ( j - > type , j - > unit - > meta . job - > type ) ;
2010-01-29 04:26:30 +01:00
if ( ! stops_running_service & & ! changes_existing_job )
2010-01-21 02:59:12 +01:00
continue ;
2010-01-29 04:26:30 +01:00
if ( stops_running_service )
2010-07-07 17:57:54 +02:00
log_info ( " %s/%s would stop a running service. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 04:26:30 +01:00
if ( changes_existing_job )
2010-07-07 17:57:54 +02:00
log_info ( " %s/%s would change existing job. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 04:26:30 +01:00
2010-01-21 02:59:12 +01:00
/* Ok, let's get rid of this */
2010-07-07 17:57:54 +02:00
log_info ( " Deleting %s/%s to minimize impact. " , j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-01-29 04:26:30 +01:00
2010-04-06 02:43:58 +02:00
transaction_delete_job ( m , j , true ) ;
2010-01-21 02:59:12 +01:00
again = true ;
break ;
}
if ( again )
break ;
}
} while ( again ) ;
}
2010-04-22 02:42:59 +02:00
static int transaction_apply ( Manager * m ) {
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-20 02:12:51 +01:00
Job * j ;
int r ;
2010-01-21 00:51:37 +01:00
/* Moves the transaction jobs to the set of active jobs */
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-21 02:59:12 +01:00
/* Assume merged */
assert ( ! j - > transaction_prev ) ;
assert ( ! j - > transaction_next ) ;
2010-01-26 19:25:02 +01:00
if ( j - > installed )
2010-01-20 02:12:51 +01:00
continue ;
if ( ( r = hashmap_put ( m - > jobs , UINT32_TO_PTR ( j - > id ) , j ) ) < 0 )
2010-01-19 04:15:20 +01:00
goto rollback ;
}
2010-01-20 02:12:51 +01:00
while ( ( j = hashmap_steal_first ( m - > transaction_jobs ) ) ) {
2010-01-26 19:25:02 +01:00
if ( j - > installed )
2010-01-20 02:12:51 +01:00
continue ;
2010-01-26 21:39:06 +01:00
if ( j - > unit - > meta . job )
job_free ( j - > unit - > meta . job ) ;
2010-01-19 04:15:20 +01:00
2010-01-26 21:39:06 +01:00
j - > unit - > meta . job = j ;
2010-01-26 19:25:02 +01:00
j - > installed = true ;
2010-01-19 04:15:20 +01:00
2010-01-20 02:12:51 +01: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 00:38:41 +01:00
job_add_to_run_queue ( j ) ;
job_add_to_dbus_queue ( j ) ;
2010-07-17 04:09:28 +02:00
job_start_timer ( j ) ;
2010-01-27 01:39:24 +01:00
}
/* As last step, kill all remaining job dependencies. */
2010-01-27 01:43:18 +01:00
transaction_clean_dependencies ( m ) ;
2010-01-21 00:51:37 +01:00
2010-01-19 04:15:20 +01:00
return 0 ;
rollback :
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , m - > transaction_jobs , i ) {
2010-01-26 19:25:02 +01:00
if ( j - > installed )
2010-01-20 02:12:51 +01:00
continue ;
hashmap_remove ( m - > jobs , UINT32_TO_PTR ( j - > id ) ) ;
}
return r ;
}
2010-07-08 02:43:18 +02:00
static int transaction_activate ( Manager * m , JobMode mode , DBusError * e ) {
2010-01-20 02:12:51 +01:00
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 02:59:12 +01: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 02:43:58 +02:00
/* Third step: Drop redundant jobs */
transaction_drop_redundant ( m ) ;
2010-01-21 00:51:37 +01:00
for ( ; ; ) {
2010-04-06 02:43:58 +02:00
/* Fourth step: Let's remove unneeded jobs that might
2010-01-21 00:51:37 +01:00
* be lurking . */
transaction_collect_garbage ( m ) ;
2010-01-20 02:12:51 +01:00
2010-04-06 02:43:58 +02:00
/* Fifth step: verify order makes sense and correct
2010-01-21 00:51:37 +01:00
* cycles if necessary and possible */
2010-07-08 02:43:18 +02:00
if ( ( r = transaction_verify_order ( m , & generation , e ) ) > = 0 )
2010-01-21 00:51:37 +01:00
break ;
2010-01-20 02:12:51 +01:00
2010-01-29 04:11:36 +01:00
if ( r ! = - EAGAIN ) {
2010-07-08 02:43:18 +02:00
log_warning ( " Requested transaction contains an unfixable cyclic ordering dependency: %s " , bus_error ( e , r ) ) ;
2010-01-21 00:51:37 +01:00
goto rollback ;
2010-01-29 04:11:36 +01:00
}
2010-01-20 02:12:51 +01:00
2010-01-21 00:51:37 +01:00
/* Let's see if the resulting transaction ordering
* graph is still cyclic . . . */
}
for ( ; ; ) {
2010-04-06 02:43:58 +02:00
/* Sixth step: let's drop unmergeable entries if
2010-01-21 00:51:37 +01:00
* necessary and possible , merge entries we can
* merge */
2010-07-08 02:43:18 +02:00
if ( ( r = transaction_merge_jobs ( m , e ) ) > = 0 )
2010-01-21 00:51:37 +01:00
break ;
2010-01-29 04:11:36 +01:00
if ( r ! = - EAGAIN ) {
2010-07-08 02:43:18 +02:00
log_warning ( " Requested transaction contains unmergable jobs: %s " , bus_error ( e , r ) ) ;
2010-01-21 00:51:37 +01:00
goto rollback ;
2010-01-29 04:11:36 +01:00
}
2010-01-21 00:51:37 +01:00
2010-04-06 02:43:58 +02:00
/* Seventh step: an entry got dropped, let's garbage
2010-01-21 00:51:37 +01:00
* collect its dependencies . */
transaction_collect_garbage ( m ) ;
/* Let's see if the resulting transaction still has
2010-01-23 01:52:57 +01:00
* unmergeable entries . . . */
2010-01-21 00:51:37 +01:00
}
2010-04-06 02:43:58 +02: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 02:12:51 +01:00
if ( mode = = JOB_FAIL )
2010-07-08 02:43:18 +02:00
if ( ( r = transaction_is_destructive ( m , e ) ) < 0 ) {
log_notice ( " Requested transaction contradicts existing jobs: %s " , bus_error ( e , r ) ) ;
2010-01-20 02:12:51 +01:00
goto rollback ;
2010-01-29 04:11:36 +01:00
}
2010-01-20 02:12:51 +01:00
2010-04-06 02:43:58 +02:00
/* Tenth step: apply changes */
2010-04-22 02:42:59 +02:00
if ( ( r = transaction_apply ( m ) ) < 0 ) {
2010-07-07 17:57:54 +02:00
log_warning ( " Failed to apply transaction: %s " , strerror ( - r ) ) ;
2010-01-20 02:12:51 +01:00
goto rollback ;
2010-01-29 04:11:36 +01:00
}
2010-01-20 02:12:51 +01:00
assert ( hashmap_isempty ( m - > transaction_jobs ) ) ;
assert ( ! m - > transaction_anchor ) ;
return 0 ;
2010-01-19 04:15:20 +01:00
2010-01-20 02:12:51 +01:00
rollback :
2010-01-19 04:15:20 +01:00
transaction_abort ( m ) ;
return r ;
}
2010-04-15 03:11:11 +02:00
static Job * transaction_add_one_job ( Manager * m , JobType type , Unit * unit , bool override , bool * is_new ) {
2010-01-20 02:12:51 +01:00
Job * j , * f ;
2009-11-18 00:42:52 +01:00
int r ;
assert ( m ) ;
2010-01-26 21:39:06 +01:00
assert ( unit ) ;
2009-11-18 00:42:52 +01:00
2010-01-20 02:12:51 +01: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 00:42:52 +01:00
2010-01-26 21:39:06 +01:00
f = hashmap_get ( m - > transaction_jobs , unit ) ;
2009-11-18 00:42:52 +01:00
2010-01-26 04:18:44 +01:00
LIST_FOREACH ( transaction , j , f ) {
2010-01-26 21:39:06 +01:00
assert ( j - > unit = = unit ) ;
2009-11-18 00:42:52 +01:00
2010-01-20 02:12:51 +01:00
if ( j - > type = = type ) {
if ( is_new )
* is_new = false ;
return j ;
}
}
2009-11-18 00:42:52 +01:00
2010-01-26 21:39:06 +01: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 02:12:51 +01:00
return NULL ;
2009-11-18 00:42:52 +01:00
2010-01-20 02:12:51 +01:00
j - > generation = 0 ;
j - > marker = NULL ;
j - > matters_to_anchor = false ;
2010-04-15 03:11:11 +02:00
j - > override = override ;
2009-11-18 00:42:52 +01:00
2010-01-26 04:18:44 +01:00
LIST_PREPEND ( Job , transaction , f , j ) ;
2010-01-26 21:39:06 +01:00
if ( ( r = hashmap_replace ( m - > transaction_jobs , unit , f ) ) < 0 ) {
2010-01-26 04:18:44 +01:00
job_free ( j ) ;
return NULL ;
}
2010-01-20 02:12:51 +01:00
if ( is_new )
* is_new = true ;
2009-11-18 00:42:52 +01:00
2010-04-15 03:11:11 +02:00
log_debug ( " Added job %s/%s to transaction. " , unit - > meta . id , job_type_to_string ( type ) ) ;
2010-04-06 02:43:58 +02:00
2010-01-20 02:12:51 +01:00
return j ;
}
2010-01-19 04:15:20 +01:00
2010-04-06 02:43:58 +02:00
void manager_transaction_unlink_job ( Manager * m , Job * j , bool delete_dependencies ) {
2010-01-20 02:12:51 +01:00
assert ( m ) ;
assert ( j ) ;
2010-01-19 04:15:20 +01:00
2010-01-20 02:12:51 +01:00
if ( j - > transaction_prev )
j - > transaction_prev - > transaction_next = j - > transaction_next ;
else if ( j - > transaction_next )
2010-01-26 21:39:06 +01:00
hashmap_replace ( m - > transaction_jobs , j - > unit , j - > transaction_next ) ;
2010-01-20 02:12:51 +01:00
else
2010-01-26 21:39:06 +01:00
hashmap_remove_value ( m - > transaction_jobs , j - > unit , j ) ;
2010-01-20 02:12:51 +01: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 19:20:15 +01:00
while ( j - > object_list ) {
Job * other = j - > object_list - > matters ? j - > object_list - > subject : NULL ;
2010-01-20 02:12:51 +01:00
job_dependency_free ( j - > object_list ) ;
2010-01-20 19:20:15 +01:00
2010-04-06 02:43:58 +02:00
if ( other & & delete_dependencies ) {
2010-08-06 02:58:46 +02:00
log_debug ( " Deleting job %s/%s as dependency of job %s/%s " ,
2010-04-15 03:11:11 +02:00
other - > unit - > meta . id , job_type_to_string ( other - > type ) ,
j - > unit - > meta . id , job_type_to_string ( j - > type ) ) ;
2010-04-06 02:43:58 +02:00
transaction_delete_job ( m , other , delete_dependencies ) ;
2010-01-20 19:20:15 +01:00
}
}
2010-01-20 02:12:51 +01:00
}
2010-04-15 03:11:11 +02:00
static int transaction_add_job_and_dependencies (
Manager * m ,
JobType type ,
Unit * unit ,
Job * by ,
bool matters ,
bool override ,
2010-08-09 22:32:30 +02:00
bool conflicts ,
2010-07-08 02:43:18 +02:00
DBusError * e ,
2010-04-15 03:11:11 +02:00
Job * * _ret ) {
2010-01-20 02:12:51 +01:00
Job * ret ;
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-26 21:39:06 +01:00
Unit * dep ;
2010-01-20 02:12:51 +01:00
int r ;
bool is_new ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
2010-01-26 21:39:06 +01:00
assert ( unit ) ;
2010-01-20 02:12:51 +01:00
2010-07-13 20:07:00 +02:00
if ( type ! = JOB_STOP & &
unit - > meta . load_state ! = UNIT_LOADED ) {
2010-07-08 04:09:17 +02:00
dbus_set_error ( e , BUS_ERROR_LOAD_FAILED , " Unit %s failed to load. See logs for details. " , unit - > meta . id ) ;
2010-01-20 19:20:41 +01:00
return - EINVAL ;
2010-07-08 02:43:18 +02:00
}
2010-01-20 19:20:41 +01:00
2010-07-08 02:43:18 +02:00
if ( ! unit_job_is_applicable ( unit , type ) ) {
dbus_set_error ( e , BUS_ERROR_JOB_TYPE_NOT_APPLICABLE , " Job type %s is not applicable for unit %s. " , job_type_to_string ( type ) , unit - > meta . id ) ;
2010-01-21 03:26:34 +01:00
return - EBADR ;
2010-07-08 02:43:18 +02:00
}
2010-01-21 03:26:34 +01:00
2010-01-20 02:12:51 +01:00
/* First add the job. */
2010-04-15 03:11:11 +02:00
if ( ! ( ret = transaction_add_one_job ( m , type , unit , override , & is_new ) ) )
2010-01-20 02:12:51 +01:00
return - ENOMEM ;
/* Then, add a link to the job. */
2010-08-09 22:32:30 +02:00
if ( ! job_dependency_new ( by , ret , matters , conflicts ) )
2010-01-20 02:12:51 +01:00
return - ENOMEM ;
if ( is_new ) {
/* Finally, recursively add in all dependencies. */
if ( type = = JOB_START | | type = = JOB_RELOAD_OR_START ) {
2010-01-26 21:39:06 +01:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUIRES ] , i )
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , true , override , false , e , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 02:12:51 +01:00
goto fail ;
2010-04-15 03:11:11 +02:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUIRES_OVERRIDABLE ] , i )
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , ! override , override , false , e , NULL ) ) < 0 & & r ! = - EBADR ) {
2010-07-11 00:52:19 +02:00
log_warning ( " Cannot add dependency job for unit %s, ignoring: %s " , dep - > meta . id , bus_error ( e , r ) ) ;
dbus_error_free ( e ) ;
}
2010-04-15 03:11:11 +02:00
2010-01-26 21:39:06 +01:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_WANTS ] , i )
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_START , dep , ret , false , false , false , e , NULL ) ) < 0 ) {
2010-07-11 00:52:19 +02:00
log_warning ( " Cannot add dependency job for unit %s, ignoring: %s " , dep - > meta . id , bus_error ( e , r ) ) ;
dbus_error_free ( e ) ;
}
2010-04-15 03:11:11 +02:00
2010-01-26 21:39:06 +01:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUISITE ] , i )
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_VERIFY_ACTIVE , dep , ret , true , override , false , e , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 02:12:51 +01:00
goto fail ;
2010-04-15 03:11:11 +02:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUISITE_OVERRIDABLE ] , i )
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_VERIFY_ACTIVE , dep , ret , ! override , override , false , e , NULL ) ) < 0 & & r ! = - EBADR ) {
2010-07-11 00:52:19 +02:00
log_warning ( " Cannot add dependency job for unit %s, ignoring: %s " , dep - > meta . id , bus_error ( e , r ) ) ;
dbus_error_free ( e ) ;
}
2010-04-15 03:11:11 +02:00
2010-01-26 21:39:06 +01:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_CONFLICTS ] , i )
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_STOP , dep , ret , true , override , true , e , NULL ) ) < 0 & & r ! = - EBADR )
goto fail ;
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_CONFLICTED_BY ] , i )
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_STOP , dep , ret , true , override , false , e , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 02:12:51 +01:00
goto fail ;
} else if ( type = = JOB_STOP | | type = = JOB_RESTART | | type = = JOB_TRY_RESTART ) {
2010-01-26 21:39:06 +01:00
SET_FOREACH ( dep , ret - > unit - > meta . dependencies [ UNIT_REQUIRED_BY ] , i )
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , type , dep , ret , true , override , false , e , NULL ) ) < 0 & & r ! = - EBADR )
2010-01-20 02:12:51 +01:00
goto fail ;
}
/* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
}
2009-11-18 00:42:52 +01:00
2010-02-12 02:21:08 +01:00
if ( _ret )
* _ret = ret ;
2009-11-18 00:42:52 +01:00
return 0 ;
fail :
2010-01-20 02:12:51 +01:00
return r ;
}
2010-04-22 02:42:59 +02: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 */
2010-07-01 00:31:53 +02:00
if ( UNIT_IS_INACTIVE_OR_MAINTENANCE ( unit_active_state ( u ) ) )
2010-04-22 02:42:59 +02:00
continue ;
/* Is there already something listed for this? */
if ( hashmap_get ( m - > transaction_jobs , u ) )
continue ;
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , JOB_STOP , u , NULL , true , false , false , NULL , NULL ) ) < 0 )
2010-04-22 02:42:59 +02:00
log_warning ( " Cannot add isolate job for unit %s, ignoring: %s " , u - > meta . id , strerror ( - r ) ) ;
}
return 0 ;
}
2010-07-08 02:43:18 +02:00
int manager_add_job ( Manager * m , JobType type , Unit * unit , JobMode mode , bool override , DBusError * e , Job * * _ret ) {
2010-01-20 02:12:51 +01:00
int r ;
Job * ret ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
2010-01-26 21:39:06 +01:00
assert ( unit ) ;
2010-01-20 02:12:51 +01:00
assert ( mode < _JOB_MODE_MAX ) ;
2009-11-18 00:42:52 +01:00
2010-07-08 02:43:18 +02:00
if ( mode = = JOB_ISOLATE & & type ! = JOB_START ) {
dbus_set_error ( e , BUS_ERROR_INVALID_JOB_MODE , " Isolate is only valid for start. " ) ;
2010-04-22 02:42:59 +02:00
return - EINVAL ;
2010-07-08 02:43:18 +02:00
}
2010-04-22 02:42:59 +02:00
2010-04-15 03:11:11 +02:00
log_debug ( " Trying to enqueue job %s/%s " , unit - > meta . id , job_type_to_string ( type ) ) ;
2010-01-29 04:11:36 +01:00
2010-08-09 22:32:30 +02:00
if ( ( r = transaction_add_job_and_dependencies ( m , type , unit , NULL , true , override , false , e , & ret ) ) < 0 ) {
2010-01-19 04:15:20 +01:00
transaction_abort ( m ) ;
2010-01-20 02:12:51 +01:00
return r ;
}
2010-01-19 04:15:20 +01:00
2010-04-22 02:42:59 +02:00
if ( mode = = JOB_ISOLATE )
if ( ( r = transaction_add_isolate_jobs ( m ) ) < 0 ) {
transaction_abort ( m ) ;
return r ;
}
2010-07-08 02:43:18 +02:00
if ( ( r = transaction_activate ( m , mode , e ) ) < 0 )
2010-01-20 02:12:51 +01:00
return r ;
2010-04-15 03:11:11 +02:00
log_debug ( " Enqueued job %s/%s as %u " , unit - > meta . id , job_type_to_string ( type ) , ( unsigned ) ret - > id ) ;
2010-01-29 03:18:09 +01:00
2010-01-20 02:12:51 +01:00
if ( _ret )
* _ret = ret ;
2009-11-18 00:42:52 +01:00
2010-01-20 02:12:51 +01:00
return 0 ;
}
2009-11-18 00:42:52 +01:00
2010-07-08 02:43:18 +02:00
int manager_add_job_by_name ( Manager * m , JobType type , const char * name , JobMode mode , bool override , DBusError * e , Job * * _ret ) {
2010-04-13 01:59:06 +02:00
Unit * unit ;
int r ;
assert ( m ) ;
assert ( type < _JOB_TYPE_MAX ) ;
assert ( name ) ;
assert ( mode < _JOB_MODE_MAX ) ;
2010-07-08 02:43:18 +02:00
if ( ( r = manager_load_unit ( m , name , NULL , NULL , & unit ) ) < 0 )
2010-04-13 01:59:06 +02:00
return r ;
2010-07-08 02:43:18 +02:00
return manager_add_job ( m , type , unit , mode , override , e , _ret ) ;
2010-04-13 01:59:06 +02:00
}
2009-11-18 00:42:52 +01:00
Job * manager_get_job ( Manager * m , uint32_t id ) {
assert ( m ) ;
return hashmap_get ( m - > jobs , UINT32_TO_PTR ( id ) ) ;
}
2010-01-26 21:39:06 +01:00
Unit * manager_get_unit ( Manager * m , const char * name ) {
2009-11-18 00:42:52 +01:00
assert ( m ) ;
assert ( name ) ;
2010-01-26 21:39:06 +01:00
return hashmap_get ( m - > units , name ) ;
2009-11-18 00:42:52 +01:00
}
2010-02-05 00:38:41 +01:00
unsigned manager_dispatch_load_queue ( Manager * m ) {
2009-11-18 00:42:52 +01:00
Meta * meta ;
2010-02-05 00:38:41 +01:00
unsigned n = 0 ;
2009-11-18 00:42:52 +01:00
assert ( m ) ;
2009-11-19 02:52:17 +01:00
/* Make sure we are not run recursively */
if ( m - > dispatching_load_queue )
2010-02-05 00:38:41 +01:00
return 0 ;
2009-11-19 02:52:17 +01:00
m - > dispatching_load_queue = true ;
2010-01-26 21:39:06 +01:00
/* Dispatches the load queue. Takes a unit from the queue and
2009-11-18 00:42:52 +01:00
* tries to load its data until the queue is empty */
while ( ( meta = m - > load_queue ) ) {
2010-01-26 04:18:44 +01:00
assert ( meta - > in_load_queue ) ;
2010-06-19 16:57:29 +02:00
unit_load ( ( Unit * ) meta ) ;
2010-02-05 00:38:41 +01:00
n + + ;
2009-11-18 00:42:52 +01:00
}
2009-11-19 02:52:17 +01:00
m - > dispatching_load_queue = false ;
2010-02-05 00:38:41 +01:00
return n ;
2009-11-18 00:42:52 +01:00
}
2010-07-08 02:43:18 +02:00
int manager_load_unit_prepare ( Manager * m , const char * name , const char * path , DBusError * e , Unit * * _ret ) {
2010-01-26 21:39:06 +01:00
Unit * ret ;
2009-11-18 00:42:52 +01:00
int r ;
assert ( m ) ;
2010-04-15 03:11:11 +02:00
assert ( name | | path ) ;
2009-11-18 00:42:52 +01:00
2010-04-24 04:26:33 +02:00
/* This will prepare the unit for loading, but not actually
* load anything from disk . */
2010-01-27 00:15:56 +01:00
2010-07-08 02:43:18 +02:00
if ( path & & ! is_path ( path ) ) {
dbus_set_error ( e , BUS_ERROR_INVALID_PATH , " Path %s is not absolute. " , path ) ;
2010-04-15 03:11:11 +02:00
return - EINVAL ;
2010-07-08 02:43:18 +02:00
}
2010-04-15 03:11:11 +02:00
if ( ! name )
name = file_name_from_path ( path ) ;
2010-07-08 02:43:18 +02:00
if ( ! unit_name_is_valid ( name ) ) {
dbus_set_error ( e , BUS_ERROR_INVALID_NAME , " Unit name %s is not valid. " , name ) ;
2010-04-15 03:11:11 +02:00
return - EINVAL ;
2010-07-08 02:43:18 +02:00
}
2009-11-18 00:42:52 +01:00
2010-01-26 21:39:06 +01:00
if ( ( ret = manager_get_unit ( m , name ) ) ) {
2010-01-26 04:18:44 +01:00
* _ret = ret ;
2010-05-16 04:30:45 +02:00
return 1 ;
2010-01-26 04:18:44 +01:00
}
2009-11-18 00:42:52 +01:00
2010-01-26 21:39:06 +01:00
if ( ! ( ret = unit_new ( m ) ) )
2009-11-18 00:42:52 +01:00
return - ENOMEM ;
2010-04-15 03:11:11 +02:00
if ( path )
2010-02-14 01:01:10 +01:00
if ( ! ( ret - > meta . fragment_path = strdup ( path ) ) ) {
2010-01-27 00:15:56 +01:00
unit_free ( ret ) ;
return - ENOMEM ;
}
2010-01-26 21:39:06 +01:00
if ( ( r = unit_add_name ( ret , name ) ) < 0 ) {
unit_free ( ret ) ;
2010-01-21 00:51:37 +01:00
return r ;
2009-11-18 00:42:52 +01:00
}
2010-01-26 21:39:06 +01:00
unit_add_to_load_queue ( ret ) ;
2010-02-05 00:38:41 +01:00
unit_add_to_dbus_queue ( ret ) ;
2010-05-16 04:31:07 +02:00
unit_add_to_gc_queue ( ret ) ;
2010-02-05 00:38:41 +01:00
2010-04-24 04:26:33 +02:00
if ( _ret )
* _ret = ret ;
return 0 ;
}
2010-07-08 02:43:18 +02:00
int manager_load_unit ( Manager * m , const char * name , const char * path , DBusError * e , Unit * * _ret ) {
2010-04-24 04:26:33 +02:00
int r ;
assert ( m ) ;
/* This will load the service information files, but not actually
* start any services or anything . */
2010-07-08 02:43:18 +02:00
if ( ( r = manager_load_unit_prepare ( m , name , path , e , _ret ) ) ! = 0 )
2010-04-24 04:26:33 +02:00
return r ;
2010-01-29 03:18:09 +01:00
manager_dispatch_load_queue ( m ) ;
2009-11-18 00:42:52 +01:00
2010-04-15 03:11:11 +02:00
if ( _ret )
2010-05-16 04:30:45 +02:00
* _ret = unit_follow_merge ( * _ret ) ;
2010-04-15 03:11:11 +02:00
2009-11-18 00:42:52 +01:00
return 0 ;
}
2010-01-19 00:22:34 +01:00
2010-01-20 04:02:39 +01:00
void manager_dump_jobs ( Manager * s , FILE * f , const char * prefix ) {
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-19 00:22:34 +01:00
Job * j ;
assert ( s ) ;
assert ( f ) ;
2010-01-26 04:18:44 +01:00
HASHMAP_FOREACH ( j , s - > jobs , i )
2010-01-20 04:02:39 +01:00
job_dump ( j , f , prefix ) ;
2010-01-19 00:22:34 +01:00
}
2010-01-26 21:39:06 +01:00
void manager_dump_units ( Manager * s , FILE * f , const char * prefix ) {
2010-01-26 04:18:44 +01:00
Iterator i ;
2010-01-26 21:39:06 +01:00
Unit * u ;
2010-01-19 04:15:20 +01:00
const char * t ;
2010-01-19 00:22:34 +01:00
assert ( s ) ;
assert ( f ) ;
2010-01-26 21:39:06 +01:00
HASHMAP_FOREACH_KEY ( u , t , s - > units , i )
2010-04-15 03:11:11 +02:00
if ( u - > meta . id = = t )
2010-01-26 21:39:06 +01:00
unit_dump ( u , f , prefix ) ;
2010-01-19 00:22:34 +01:00
}
2010-01-20 05:03:52 +01: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-23 22:56:47 +01:00
2010-02-05 00:38:41 +01:00
unsigned manager_dispatch_run_queue ( Manager * m ) {
2010-01-23 22:56:47 +01:00
Job * j ;
2010-02-05 00:38:41 +01:00
unsigned n = 0 ;
2010-01-23 22:56:47 +01:00
2010-01-26 04:18:44 +01:00
if ( m - > dispatching_run_queue )
2010-02-05 00:38:41 +01:00
return 0 ;
2010-01-26 04:18:44 +01:00
m - > dispatching_run_queue = true ;
2010-01-24 00:39:29 +01:00
2010-01-26 04:18:44 +01:00
while ( ( j = m - > run_queue ) ) {
2010-01-26 19:25:02 +01:00
assert ( j - > installed ) ;
2010-01-26 04:18:44 +01:00
assert ( j - > in_run_queue ) ;
job_run_and_invalidate ( j ) ;
2010-02-05 00:38:41 +01:00
n + + ;
2010-01-24 00:39:29 +01:00
}
2010-01-26 04:18:44 +01:00
m - > dispatching_run_queue = false ;
2010-02-05 00:38:41 +01: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 02:43:58 +02:00
assert ( meta - > in_dbus_queue ) ;
2010-02-05 00:38:41 +01:00
2010-06-19 16:57:29 +02:00
bus_unit_send_change_signal ( ( Unit * ) meta ) ;
2010-02-05 00:38:41 +01: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 00:39:29 +01:00
}
2010-06-16 05:10:31 +02:00
static int manager_process_notify_fd ( Manager * m ) {
ssize_t n ;
assert ( m ) ;
for ( ; ; ) {
char buf [ 4096 ] ;
struct msghdr msghdr ;
struct iovec iovec ;
struct ucred * ucred ;
union {
struct cmsghdr cmsghdr ;
uint8_t buf [ CMSG_SPACE ( sizeof ( struct ucred ) ) ] ;
} control ;
Unit * u ;
char * * tags ;
zero ( iovec ) ;
iovec . iov_base = buf ;
iovec . iov_len = sizeof ( buf ) - 1 ;
zero ( control ) ;
zero ( msghdr ) ;
msghdr . msg_iov = & iovec ;
msghdr . msg_iovlen = 1 ;
msghdr . msg_control = & control ;
msghdr . msg_controllen = sizeof ( control ) ;
if ( ( n = recvmsg ( m - > notify_watch . fd , & msghdr , MSG_DONTWAIT ) ) < = 0 ) {
if ( n > = 0 )
return - EIO ;
if ( errno = = EAGAIN )
break ;
return - errno ;
}
if ( msghdr . msg_controllen < CMSG_LEN ( sizeof ( struct ucred ) ) | |
control . cmsghdr . cmsg_level ! = SOL_SOCKET | |
control . cmsghdr . cmsg_type ! = SCM_CREDENTIALS | |
control . cmsghdr . cmsg_len ! = CMSG_LEN ( sizeof ( struct ucred ) ) ) {
log_warning ( " Received notify message without credentials. Ignoring. " ) ;
continue ;
}
ucred = ( struct ucred * ) CMSG_DATA ( & control . cmsghdr ) ;
2010-07-11 00:50:49 +02:00
if ( ! ( u = hashmap_get ( m - > watch_pids , LONG_TO_PTR ( ucred - > pid ) ) ) )
2010-06-16 05:10:31 +02:00
if ( ! ( u = cgroup_unit_by_pid ( m , ucred - > pid ) ) ) {
log_warning ( " Cannot find unit for notify message of PID %lu. " , ( unsigned long ) ucred - > pid ) ;
continue ;
}
2010-06-18 23:13:40 +02:00
assert ( ( size_t ) n < sizeof ( buf ) ) ;
buf [ n ] = 0 ;
2010-06-16 05:10:31 +02:00
if ( ! ( tags = strv_split ( buf , " \n \r " ) ) )
return - ENOMEM ;
log_debug ( " Got notification message for unit %s " , u - > meta . id ) ;
if ( UNIT_VTABLE ( u ) - > notify_message )
2010-06-18 23:12:48 +02:00
UNIT_VTABLE ( u ) - > notify_message ( u , ucred - > pid , tags ) ;
2010-06-16 05:10:31 +02:00
strv_free ( tags ) ;
}
return 0 ;
}
2010-01-26 04:18:44 +01:00
static int manager_dispatch_sigchld ( Manager * m ) {
2010-01-24 00:39:29 +01:00
assert ( m ) ;
for ( ; ; ) {
siginfo_t si ;
2010-01-26 21:39:06 +01:00
Unit * u ;
2010-06-16 05:10:31 +02:00
int r ;
2010-01-24 00:39:29 +01:00
zero ( si ) ;
2010-04-13 02:05:27 +02: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 04:31:52 +01:00
if ( errno = = ECHILD )
break ;
2010-04-13 02:05:27 +02:00
if ( errno = = EINTR )
continue ;
2010-01-24 00:39:29 +01:00
return - errno ;
2010-01-27 04:31:52 +01:00
}
2010-01-24 00:39:29 +01:00
2010-04-13 02:05:27 +02:00
if ( si . si_pid < = 0 )
2010-01-24 00:39:29 +01:00
break ;
2010-04-13 02:10:17 +02:00
if ( si . si_code = = CLD_EXITED | | si . si_code = = CLD_KILLED | | si . si_code = = CLD_DUMPED ) {
2010-04-13 02:05:27 +02:00
char * name = NULL ;
get_process_name ( si . si_pid , & name ) ;
2010-06-19 04:35:52 +02:00
log_debug ( " Got SIGCHLD for process %lu (%s) " , ( unsigned long ) si . si_pid , strna ( name ) ) ;
2010-04-13 02:05:27 +02:00
free ( name ) ;
}
2010-06-16 05:10:31 +02:00
/* Let's flush any message the dying child might still
* have queued for us . This ensures that the process
* still exists in / proc so that we can figure out
* which cgroup and hence unit it belongs to . */
if ( ( r = manager_process_notify_fd ( m ) ) < 0 )
return r ;
/* And now figure out the unit this belongs to */
2010-07-11 00:50:49 +02:00
if ( ! ( u = hashmap_get ( m - > watch_pids , LONG_TO_PTR ( si . si_pid ) ) ) )
2010-06-16 05:10:31 +02:00
u = cgroup_unit_by_pid ( m , si . si_pid ) ;
2010-04-13 02:05:27 +02:00
/* 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 04:18:44 +01:00
if ( si . si_code ! = CLD_EXITED & & si . si_code ! = CLD_KILLED & & si . si_code ! = CLD_DUMPED )
continue ;
2010-06-19 04:35:52 +02:00
log_debug ( " Child %lu died (code=%s, status=%i/%s) " ,
( long unsigned ) si . si_pid ,
2010-04-13 02:05:27 +02:00
sigchld_code_to_string ( si . si_code ) ,
si . si_status ,
2010-07-10 15:42:24 +02:00
strna ( si . si_code = = CLD_EXITED ? exit_status_to_string ( si . si_status ) : signal_to_string ( si . si_status ) ) ) ;
2010-01-27 04:31:52 +01:00
2010-06-16 05:10:31 +02:00
if ( ! u )
2010-01-24 00:39:29 +01:00
continue ;
2010-07-11 00:50:49 +02:00
log_debug ( " Child %lu belongs to %s " , ( long unsigned ) si . si_pid , u - > meta . id ) ;
2010-04-07 03:19:00 +02:00
2010-07-11 00:50:49 +02:00
hashmap_remove ( m - > watch_pids , LONG_TO_PTR ( si . si_pid ) ) ;
2010-01-26 21:39:06 +01:00
UNIT_VTABLE ( u ) - > sigchld_event ( u , si . si_pid , si . si_code , si . si_status ) ;
2010-01-24 00:39:29 +01:00
}
return 0 ;
}
2010-06-17 23:22:56 +02:00
static int manager_start_target ( Manager * m , const char * name , JobMode mode ) {
2010-04-13 01:59:06 +02:00
int r ;
2010-07-08 02:43:18 +02:00
DBusError error ;
dbus_error_init ( & error ) ;
2010-07-10 04:50:19 +02:00
log_info ( " Activating special unit %s " , name ) ;
2010-07-08 02:43:18 +02:00
if ( ( r = manager_add_job_by_name ( m , JOB_START , name , mode , true , & error , NULL ) ) < 0 )
log_error ( " Failed to enqueue %s job: %s " , name , bus_error ( & error , r ) ) ;
2010-04-13 01:59:06 +02:00
2010-07-08 02:43:18 +02:00
dbus_error_free ( & error ) ;
2010-05-24 22:31:38 +02:00
return r ;
2010-04-13 01:59:06 +02:00
}
2010-04-21 03:27:44 +02:00
static int manager_process_signal_fd ( Manager * m ) {
2010-01-24 00:39:29 +01:00
ssize_t n ;
struct signalfd_siginfo sfsi ;
bool sigchld = false ;
assert ( m ) ;
for ( ; ; ) {
2010-01-27 04:31:52 +01:00
if ( ( n = read ( m - > signal_watch . fd , & sfsi , sizeof ( sfsi ) ) ) ! = sizeof ( sfsi ) ) {
2010-01-24 00:39:29 +01:00
if ( n > = 0 )
return - EIO ;
if ( errno = = EAGAIN )
2010-01-27 04:31:52 +01:00
break ;
2010-01-24 00:39:29 +01:00
return - errno ;
}
2010-07-10 04:50:19 +02:00
log_debug ( " Received SIG%s " , strna ( signal_to_string ( sfsi . ssi_signo ) ) ) ;
2010-01-27 04:36:30 +01:00
switch ( sfsi . ssi_signo ) {
2010-04-13 02:05:27 +02:00
case SIGCHLD :
2010-01-24 00:39:29 +01:00
sigchld = true ;
2010-01-27 04:36:30 +01:00
break ;
2010-01-27 05:31:53 +01:00
case SIGTERM :
2010-06-19 03:15:59 +02:00
if ( m - > running_as = = MANAGER_SYSTEM ) {
2010-04-24 04:26:33 +02:00
/* This is for compatibility with the
* original sysvinit */
2010-04-24 00:02:12 +02:00
m - > exit_code = MANAGER_REEXECUTE ;
2010-05-24 22:31:38 +02:00
break ;
}
2010-02-13 01:17:08 +01:00
2010-05-24 22:31:38 +02:00
/* Fall through */
2010-04-24 00:02:12 +02:00
case SIGINT :
2010-06-19 03:15:59 +02:00
if ( m - > running_as = = MANAGER_SYSTEM ) {
2010-06-17 23:22:56 +02:00
manager_start_target ( m , SPECIAL_CTRL_ALT_DEL_TARGET , JOB_REPLACE ) ;
2010-02-13 01:17:08 +01:00
break ;
}
2010-05-24 22:31:38 +02:00
/* Run the exit target if there is one, if not, just exit. */
2010-06-17 23:22:56 +02:00
if ( manager_start_target ( m , SPECIAL_EXIT_SERVICE , JOB_REPLACE ) < 0 ) {
2010-05-24 22:31:38 +02:00
m - > exit_code = MANAGER_EXIT ;
return 0 ;
}
break ;
2010-02-13 01:17:08 +01:00
2010-04-13 01:59:06 +02:00
case SIGWINCH :
2010-06-19 03:15:59 +02:00
if ( m - > running_as = = MANAGER_SYSTEM )
2010-06-17 23:22:56 +02:00
manager_start_target ( m , SPECIAL_KBREQUEST_TARGET , JOB_REPLACE ) ;
2010-02-13 01:17:08 +01:00
2010-04-13 01:59:06 +02:00
/* This is a nop on non-init */
break ;
2010-02-13 01:17:08 +01:00
2010-04-13 01:59:06 +02:00
case SIGPWR :
2010-06-19 03:15:59 +02:00
if ( m - > running_as = = MANAGER_SYSTEM )
2010-06-17 23:22:56 +02:00
manager_start_target ( m , SPECIAL_SIGPWR_TARGET , JOB_REPLACE ) ;
2010-02-13 01:17:08 +01:00
2010-04-13 01:59:06 +02:00
/* This is a nop on non-init */
2010-02-13 01:17:08 +01:00
break ;
2010-01-27 05:31:53 +01:00
2010-04-24 04:27:05 +02:00
case SIGUSR1 : {
2010-04-13 03:20:22 +02:00
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... " ) ;
2010-06-19 03:04:04 +02:00
bus_init ( m ) ;
2010-04-13 03:20:22 +02:00
}
if ( ! u | | ! UNIT_IS_ACTIVE_OR_ACTIVATING ( unit_active_state ( u ) ) ) {
log_info ( " Loading D-Bus service... " ) ;
2010-06-17 23:22:56 +02:00
manager_start_target ( m , SPECIAL_DBUS_SERVICE , JOB_REPLACE ) ;
2010-04-13 03:20:22 +02:00
}
break ;
}
2010-06-04 19:45:53 +02:00
case SIGUSR2 : {
FILE * f ;
char * dump = NULL ;
size_t size ;
if ( ! ( f = open_memstream ( & dump , & size ) ) ) {
log_warning ( " Failed to allocate memory stream. " ) ;
break ;
}
manager_dump_units ( m , f , " \t " ) ;
manager_dump_jobs ( m , f , " \t " ) ;
if ( ferror ( f ) ) {
fclose ( f ) ;
free ( dump ) ;
log_warning ( " Failed to write status stream " ) ;
break ;
}
fclose ( f ) ;
log_dump ( LOG_INFO , dump ) ;
free ( dump ) ;
2010-04-24 04:27:05 +02:00
break ;
2010-06-04 19:45:53 +02:00
}
2010-04-24 04:27:05 +02:00
2010-04-21 03:27:44 +02:00
case SIGHUP :
m - > exit_code = MANAGER_RELOAD ;
break ;
2010-06-17 23:22:56 +02:00
default : {
static const char * const table [ ] = {
[ 0 ] = SPECIAL_DEFAULT_TARGET ,
[ 1 ] = SPECIAL_RESCUE_TARGET ,
2010-07-12 23:49:20 +02:00
[ 2 ] = SPECIAL_EMERGENCY_TARGET ,
2010-06-17 23:22:56 +02:00
[ 3 ] = SPECIAL_HALT_TARGET ,
[ 4 ] = SPECIAL_POWEROFF_TARGET ,
[ 5 ] = SPECIAL_REBOOT_TARGET
} ;
if ( ( int ) sfsi . ssi_signo > = SIGRTMIN + 0 & &
( int ) sfsi . ssi_signo < SIGRTMIN + ( int ) ELEMENTSOF ( table ) ) {
manager_start_target ( m , table [ sfsi . ssi_signo - SIGRTMIN ] ,
2010-06-24 03:07:43 +02:00
( sfsi . ssi_signo = = 1 | | sfsi . ssi_signo = = 2 ) ? JOB_ISOLATE : JOB_REPLACE ) ;
2010-06-17 23:22:56 +02:00
break ;
}
2010-07-10 04:50:19 +02:00
log_warning ( " Got unhandled signal <%s>. " , strna ( signal_to_string ( sfsi . ssi_signo ) ) ) ;
2010-01-27 04:36:30 +01:00
}
2010-06-17 23:22:56 +02:00
}
2010-01-24 00:39:29 +01:00
}
if ( sigchld )
2010-01-26 04:18:44 +01:00
return manager_dispatch_sigchld ( m ) ;
return 0 ;
}
2010-04-21 03:27:44 +02:00
static int process_event ( Manager * m , struct epoll_event * ev ) {
2010-01-26 04:18:44 +01:00
int r ;
2010-01-27 04:31:52 +01:00
Watch * w ;
2010-01-26 04:18:44 +01:00
assert ( m ) ;
assert ( ev ) ;
2010-01-27 04:31:52 +01:00
assert ( w = ev - > data . ptr ) ;
2010-01-26 04:18:44 +01:00
2010-01-27 04:31:52 +01:00
switch ( w - > type ) {
2010-01-26 04:18:44 +01:00
2010-01-29 06:04:08 +01:00
case WATCH_SIGNAL :
2010-01-26 04:18:44 +01:00
2010-01-27 04:31:52 +01:00
/* An incoming signal? */
2010-01-29 06:45:59 +01:00
if ( ev - > events ! = EPOLLIN )
2010-01-27 04:31:52 +01:00
return - EINVAL ;
2010-01-26 04:18:44 +01:00
2010-04-21 03:27:44 +02:00
if ( ( r = manager_process_signal_fd ( m ) ) < 0 )
2010-01-27 04:31:52 +01:00
return r ;
2010-01-26 04:18:44 +01:00
2010-01-27 04:31:52 +01:00
break ;
2010-01-26 04:18:44 +01:00
2010-06-16 05:10:31 +02:00
case WATCH_NOTIFY :
/* An incoming daemon notification event? */
if ( ev - > events ! = EPOLLIN )
return - EINVAL ;
if ( ( r = manager_process_notify_fd ( m ) ) < 0 )
return r ;
break ;
2010-01-27 04:31:52 +01:00
case WATCH_FD :
2010-01-26 04:18:44 +01:00
2010-01-27 04:31:52 +01:00
/* Some fd event, to be dispatched to the units */
2010-02-01 03:33:24 +01:00
UNIT_VTABLE ( w - > data . unit ) - > fd_event ( w - > data . unit , w - > fd , ev - > events , w ) ;
2010-01-27 04:31:52 +01:00
break ;
2010-01-26 04:18:44 +01:00
2010-07-17 04:09:28 +02:00
case WATCH_UNIT_TIMER :
case WATCH_JOB_TIMER : {
2010-01-27 04:31:52 +01:00
uint64_t v ;
ssize_t k ;
2010-01-26 04:18:44 +01:00
2010-01-27 04:31:52 +01:00
/* Some timer event, to be dispatched to the units */
2010-02-05 00:40:39 +01:00
if ( ( k = read ( w - > fd , & v , sizeof ( v ) ) ) ! = sizeof ( v ) ) {
2010-01-26 04:18:44 +01:00
2010-01-27 04:31:52 +01:00
if ( k < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) )
break ;
2010-01-26 04:18:44 +01:00
2010-01-27 04:31:52 +01:00
return k < 0 ? - errno : - EIO ;
2010-01-26 04:18:44 +01:00
}
2010-07-17 04:09:28 +02:00
if ( w - > type = = WATCH_UNIT_TIMER )
UNIT_VTABLE ( w - > data . unit ) - > timer_event ( w - > data . unit , v , w ) ;
else
job_timer_event ( w - > data . job , v , w ) ;
2010-01-27 04:31:52 +01:00
break ;
}
2010-01-29 06:04:08 +01:00
case WATCH_MOUNT :
/* Some mount table change, intended for the mount subsystem */
mount_fd_event ( m , ev - > events ) ;
break ;
2010-01-29 06:45:59 +01:00
case WATCH_UDEV :
/* Some notification from udev, intended for the device subsystem */
device_fd_event ( m , ev - > events ) ;
break ;
2010-02-01 03:33:24 +01: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 04:31:52 +01:00
default :
assert_not_reached ( " Unknown epoll event type. " ) ;
2010-01-26 04:18:44 +01:00
}
2010-01-24 00:39:29 +01:00
return 0 ;
}
int manager_loop ( Manager * m ) {
int r ;
2010-02-01 03:33:24 +01:00
RATELIMIT_DEFINE ( rl , 1 * USEC_PER_SEC , 1000 ) ;
2010-01-24 00:39:29 +01:00
assert ( m ) ;
2010-04-21 03:27:44 +02:00
m - > exit_code = MANAGER_RUNNING ;
2010-01-24 00:39:29 +01:00
2010-07-11 00:52:00 +02:00
/* Release the path cache */
set_free_free ( m - > unit_path_cache ) ;
m - > unit_path_cache = NULL ;
2010-05-18 04:16:33 +02:00
/* There might still be some zombies hanging around from
* before we were exec ( ) ' ed . Leat ' s reap them */
if ( ( r = manager_dispatch_sigchld ( m ) ) < 0 )
return r ;
2010-04-21 03:27:44 +02:00
while ( m - > exit_code = = MANAGER_RUNNING ) {
2010-01-27 22:40:10 +01:00
struct epoll_event event ;
int n ;
2010-01-24 00:39:29 +01:00
2010-02-01 03:33:24 +01: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-05-16 03:57:56 +02:00
if ( manager_dispatch_load_queue ( m ) > 0 )
2010-04-06 02:43:58 +02:00
continue ;
2010-05-16 03:57:56 +02:00
if ( manager_dispatch_run_queue ( m ) > 0 )
2010-04-21 06:01:13 +02:00
continue ;
2010-05-16 03:57:56 +02:00
if ( bus_dispatch ( m ) > 0 )
2010-02-05 00:38:41 +01:00
continue ;
2010-01-26 04:18:44 +01:00
2010-05-16 03:57:56 +02:00
if ( manager_dispatch_cleanup_queue ( m ) > 0 )
2010-02-05 00:38:41 +01:00
continue ;
2010-05-16 03:57:56 +02:00
if ( manager_dispatch_gc_queue ( m ) > 0 )
2010-02-05 00:38:41 +01:00
continue ;
if ( manager_dispatch_dbus_queue ( m ) > 0 )
2010-02-01 03:33:24 +01:00
continue ;
2010-01-27 22:40:10 +01:00
if ( ( n = epoll_wait ( m - > epoll_fd , & event , 1 , - 1 ) ) < 0 ) {
2010-01-24 00:39:29 +01:00
2010-04-23 22:29:35 +02:00
if ( errno = = EINTR )
2010-01-24 00:39:29 +01:00
continue ;
return - errno ;
}
2010-01-27 22:40:10 +01:00
assert ( n = = 1 ) ;
2010-01-27 04:36:30 +01:00
2010-04-21 03:27:44 +02:00
if ( ( r = process_event ( m , & event ) ) < 0 )
2010-01-27 22:40:10 +01:00
return r ;
2010-04-21 03:27:44 +02:00
}
2010-01-27 22:40:10 +01:00
2010-04-21 03:27:44 +02:00
return m - > exit_code ;
2010-01-23 22:56:47 +01:00
}
2010-02-01 03:33:24 +01: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 12:42:08 +01: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 21:57:39 +01:00
2010-08-11 01:43:23 +02:00
void manager_send_unit_audit ( Manager * m , Unit * u , int type , bool success ) {
2010-04-10 17:53:17 +02:00
2010-08-11 01:43:23 +02:00
# ifdef HAVE_AUDIT
char * p ;
2010-04-10 17:53:17 +02:00
2010-08-11 01:43:23 +02:00
if ( m - > audit_fd < 0 )
2010-04-10 17:53:17 +02:00
return ;
2010-08-11 01:43:23 +02:00
if ( ! ( p = unit_name_to_prefix_and_instance ( u - > meta . id ) ) ) {
log_error ( " Failed to allocate unit name for audit message: %s " , strerror ( ENOMEM ) ) ;
2010-04-10 17:53:17 +02:00
return ;
}
2010-08-11 01:43:23 +02:00
if ( audit_log_user_comm_message ( m - > audit_fd , type , " " , p , NULL , NULL , NULL , success ) < 0 )
log_error ( " Failed to send audit message: %m " ) ;
2010-04-10 17:53:17 +02:00
2010-08-11 01:43:23 +02:00
free ( p ) ;
# endif
2010-04-10 17:53:17 +02:00
}
2010-04-15 23:16:16 +02: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-07-20 20:54:33 +02:00
int manager_open_serialization ( Manager * m , FILE * * _f ) {
2010-04-21 03:27:44 +02:00
char * path ;
mode_t saved_umask ;
int fd ;
FILE * f ;
assert ( _f ) ;
2010-07-20 20:54:33 +02:00
if ( m - > running_as = = MANAGER_SYSTEM ) {
mkdir_p ( " /dev/.systemd " , 0755 ) ;
if ( asprintf ( & path , " /dev/.systemd/dump-%lu-XXXXXX " , ( unsigned long ) getpid ( ) ) < 0 )
return - ENOMEM ;
} else {
if ( asprintf ( & path , " /tmp/systemd-dump-%lu-XXXXXX " , ( unsigned long ) getpid ( ) ) < 0 )
return - ENOMEM ;
}
2010-04-21 03:27:44 +02:00
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... " ) ;
2010-07-13 19:01:20 +02:00
m - > n_deserializing + + ;
2010-07-10 04:51:03 +02:00
2010-04-21 03:27:44 +02:00
for ( ; ; ) {
Unit * u ;
char name [ UNIT_NAME_MAX + 2 ] ;
/* Start marker */
if ( ! fgets ( name , sizeof ( name ) , f ) ) {
if ( feof ( f ) )
break ;
2010-07-10 04:51:03 +02:00
r = - errno ;
goto finish ;
2010-04-21 03:27:44 +02:00
}
char_array_0 ( name ) ;
2010-07-08 02:43:18 +02:00
if ( ( r = manager_load_unit ( m , strstrip ( name ) , NULL , NULL , & u ) ) < 0 )
2010-07-10 04:51:03 +02:00
goto finish ;
2010-04-21 03:27:44 +02:00
if ( ( r = unit_deserialize ( u , f , fds ) ) < 0 )
2010-07-10 04:51:03 +02:00
goto finish ;
2010-04-21 03:27:44 +02:00
}
2010-07-10 04:51:03 +02:00
if ( ferror ( f ) ) {
r = - EIO ;
goto finish ;
}
2010-04-21 03:27:44 +02:00
2010-07-10 04:51:03 +02:00
r = 0 ;
finish :
2010-07-13 19:01:20 +02:00
assert ( m - > n_deserializing > 0 ) ;
m - > n_deserializing - - ;
2010-07-10 04:51:03 +02:00
return r ;
2010-04-21 03:27:44 +02:00
}
int manager_reload ( Manager * m ) {
int r , q ;
FILE * f ;
FDSet * fds ;
assert ( m ) ;
2010-07-20 20:54:33 +02:00
if ( ( r = manager_open_serialization ( m , & f ) ) < 0 )
2010-04-21 03:27:44 +02:00
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 ) ;
2010-06-15 14:32:26 +02:00
/* Find new unit paths */
2010-06-15 14:45:15 +02:00
lookup_paths_free ( & m - > lookup_paths ) ;
if ( ( q = lookup_paths_init ( & m - > lookup_paths , m - > running_as ) ) < 0 )
2010-06-15 14:32:26 +02:00
r = q ;
2010-07-13 19:01:20 +02:00
m - > n_deserializing + + ;
2010-04-21 03:27:44 +02:00
/* 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 ;
2010-07-13 19:01:20 +02:00
assert ( m - > n_deserializing > 0 ) ;
m - > n_deserializing + + ;
2010-04-21 03:27:44 +02:00
finish :
if ( f )
fclose ( f ) ;
if ( fds )
fdset_free ( fds ) ;
return r ;
}
2010-07-07 00:00:59 +02:00
bool manager_is_booting_or_shutting_down ( Manager * m ) {
Unit * u ;
assert ( m ) ;
/* Is the initial job still around? */
if ( manager_get_job ( m , 1 ) )
return true ;
/* Is there a job for the shutdown target? */
if ( ( ( u = manager_get_unit ( m , SPECIAL_SHUTDOWN_TARGET ) ) ) )
return ! ! u - > meta . job ;
return false ;
}
2010-07-18 04:58:01 +02:00
void manager_reset_maintenance ( Manager * m ) {
Unit * u ;
Iterator i ;
assert ( m ) ;
HASHMAP_FOREACH ( u , m - > units , i )
unit_reset_maintenance ( u ) ;
}
2010-02-12 21:57:39 +01:00
static const char * const manager_running_as_table [ _MANAGER_RUNNING_AS_MAX ] = {
[ MANAGER_SYSTEM ] = " system " ,
2010-02-13 01:07:02 +01:00
[ MANAGER_SESSION ] = " session "
2010-02-12 21:57:39 +01:00
} ;
DEFINE_STRING_TABLE_LOOKUP ( manager_running_as , ManagerRunningAs ) ;