2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-02-01 05:33:24 +03:00
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
2012-04-12 02:20:58 +04:00
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2010-02-03 15:03:47 +03:00
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2012-04-12 02:20:58 +04:00
Lesser General Public License for more details .
2010-02-03 15:03:47 +03:00
2012-04-12 02:20:58 +04:00
You should have received a copy of the GNU Lesser General Public License
2010-02-03 15:03:47 +03:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
2010-02-02 14:42:08 +03:00
# include <errno.h>
2010-02-01 05:33:24 +03:00
# include "dbus.h"
# include "log.h"
2010-04-18 05:08:16 +04:00
# include "dbus-job.h"
2011-04-16 03:54:49 +04:00
# include "dbus-common.h"
2012-10-03 01:07:00 +04:00
# include "selinux-access.h"
2010-02-01 05:33:24 +03:00
2010-05-23 05:45:33 +04:00
# define BUS_JOB_INTERFACE \
" <interface name= \" org.freedesktop.systemd1.Job \" > \n " \
" <method name= \" Cancel \" /> \n " \
" <property name= \" Id \" type= \" u \" access= \" read \" /> \n " \
" <property name= \" Unit \" type= \" (so) \" access= \" read \" /> \n " \
" <property name= \" JobType \" type= \" s \" access= \" read \" /> \n " \
" <property name= \" State \" type= \" s \" access= \" read \" /> \n " \
" </interface> \n "
# define INTROSPECTION \
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
" <node> \n " \
BUS_JOB_INTERFACE \
BUS_PROPERTIES_INTERFACE \
2010-08-20 04:26:05 +04:00
BUS_PEER_INTERFACE \
2010-05-23 05:45:33 +04:00
BUS_INTROSPECTABLE_INTERFACE \
" </node> \n "
2010-11-23 23:12:11 +03:00
const char bus_job_interface [ ] _introspect_ ( " Job " ) = BUS_JOB_INTERFACE ;
2010-02-01 05:33:24 +03:00
2011-03-09 22:01:53 +03:00
# define INTERFACES_LIST \
BUS_GENERIC_INTERFACES_LIST \
" org.freedesktop.systemd1.Job \0 "
2010-08-20 04:26:05 +04:00
# define INVALIDATING_PROPERTIES \
2011-03-09 21:48:02 +03:00
" State \0 "
2010-08-20 04:26:05 +04:00
2010-04-18 05:08:16 +04:00
static DEFINE_BUS_PROPERTY_APPEND_ENUM ( bus_job_append_state , job_state , JobState ) ;
static DEFINE_BUS_PROPERTY_APPEND_ENUM ( bus_job_append_type , job_type , JobType ) ;
2010-02-02 14:42:08 +03:00
2011-04-16 03:54:49 +04:00
static int bus_job_append_unit ( DBusMessageIter * i , const char * property , void * data ) {
2010-02-02 14:42:08 +03:00
Job * j = data ;
DBusMessageIter sub ;
char * p ;
assert ( i ) ;
assert ( property ) ;
assert ( j ) ;
if ( ! dbus_message_iter_open_container ( i , DBUS_TYPE_STRUCT , NULL , & sub ) )
return - ENOMEM ;
2012-10-03 01:07:00 +04:00
p = unit_dbus_path ( j - > unit ) ;
if ( ! p )
2010-02-02 14:42:08 +03:00
return - ENOMEM ;
2012-01-15 15:04:08 +04:00
if ( ! dbus_message_iter_append_basic ( & sub , DBUS_TYPE_STRING , & j - > unit - > id ) | |
2010-02-02 14:42:08 +03:00
! dbus_message_iter_append_basic ( & sub , DBUS_TYPE_OBJECT_PATH , & p ) ) {
free ( p ) ;
return - ENOMEM ;
}
free ( p ) ;
if ( ! dbus_message_iter_close_container ( i , & sub ) )
return - ENOMEM ;
return 0 ;
}
2012-01-16 03:23:59 +04:00
static const BusProperty bus_job_properties [ ] = {
{ " Id " , bus_property_append_uint32 , " u " , offsetof ( Job , id ) } ,
{ " State " , bus_job_append_state , " s " , offsetof ( Job , state ) } ,
{ " JobType " , bus_job_append_type , " s " , offsetof ( Job , type ) } ,
{ " Unit " , bus_job_append_unit , " (so) " , 0 } ,
{ NULL , }
} ;
2010-02-02 14:42:08 +03:00
2012-01-16 03:23:59 +04:00
static DBusHandlerResult bus_job_message_dispatch ( Job * j , DBusConnection * connection , DBusMessage * message ) {
2012-10-03 01:07:00 +04:00
_cleanup_dbus_message_unref_ DBusMessage * reply = NULL ;
2010-02-03 14:37:42 +03:00
if ( dbus_message_is_method_call ( message , " org.freedesktop.systemd1.Job " , " Cancel " ) ) {
2012-10-03 01:07:00 +04:00
SELINUX_UNIT_ACCESS_CHECK ( j - > unit , connection , message , " stop " ) ;
reply = dbus_message_new_method_return ( message ) ;
if ( ! reply )
return DBUS_HANDLER_RESULT_NEED_MEMORY ;
2010-02-03 14:37:42 +03:00
2012-10-03 01:07:00 +04:00
job_finish_and_invalidate ( j , JOB_CANCELED , true ) ;
2012-01-16 03:23:59 +04:00
} else {
const BusBoundProperties bps [ ] = {
{ " org.freedesktop.systemd1.Job " , bus_job_properties , j } ,
{ NULL , }
} ;
2010-02-03 14:37:42 +03:00
2012-10-03 01:07:00 +04:00
SELINUX_UNIT_ACCESS_CHECK ( j - > unit , connection , message , " status " ) ;
2010-02-03 14:37:42 +03:00
2012-10-03 01:07:00 +04:00
return bus_default_message_handler ( connection , message , INTROSPECTION , INTERFACES_LIST , bps ) ;
2010-02-03 14:37:42 +03:00
}
2012-10-03 01:07:00 +04:00
if ( ! dbus_connection_send ( connection , reply , NULL ) )
return DBUS_HANDLER_RESULT_NEED_MEMORY ;
2010-02-03 14:37:42 +03:00
2012-10-03 01:07:00 +04:00
return DBUS_HANDLER_RESULT_HANDLED ;
2010-02-02 14:42:08 +03:00
}
2010-06-19 05:04:04 +04:00
static DBusHandlerResult bus_job_message_handler ( DBusConnection * connection , DBusMessage * message , void * data ) {
2010-02-01 05:33:24 +03:00
Manager * m = data ;
2010-02-02 14:42:08 +03:00
Job * j ;
int r ;
2012-10-03 01:07:00 +04:00
_cleanup_dbus_message_unref_ DBusMessage * reply = NULL ;
2010-02-01 05:33:24 +03:00
assert ( connection ) ;
assert ( message ) ;
assert ( m ) ;
2010-10-13 05:03:31 +04:00
if ( streq ( dbus_message_get_path ( message ) , " /org/freedesktop/systemd1/job " ) ) {
/* Be nice to gdbus and return introspection data for our mid-level paths */
if ( dbus_message_is_method_call ( message , " org.freedesktop.DBus.Introspectable " , " Introspect " ) ) {
char * introspection = NULL ;
FILE * f ;
Iterator i ;
size_t size ;
2012-10-03 01:56:54 +04:00
SELINUX_ACCESS_CHECK ( connection , message , " status " ) ;
2012-10-03 01:07:00 +04:00
reply = dbus_message_new_method_return ( message ) ;
if ( ! reply )
2010-10-13 05:03:31 +04:00
goto oom ;
/* We roll our own introspection code here, instead of
* relying on bus_default_message_handler ( ) because we
* need to generate our introspection string
* dynamically . */
2012-10-03 01:07:00 +04:00
f = open_memstream ( & introspection , & size ) ;
if ( ! f )
2010-10-13 05:03:31 +04:00
goto oom ;
fputs ( DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
" <node> \n " , f ) ;
fputs ( BUS_INTROSPECTABLE_INTERFACE , f ) ;
fputs ( BUS_PEER_INTERFACE , f ) ;
HASHMAP_FOREACH ( j , m - > jobs , i )
fprintf ( f , " <node name= \" job/%lu \" /> " , ( unsigned long ) j - > id ) ;
fputs ( " </node> \n " , f ) ;
if ( ferror ( f ) ) {
fclose ( f ) ;
free ( introspection ) ;
goto oom ;
}
fclose ( f ) ;
if ( ! introspection )
goto oom ;
if ( ! dbus_message_append_args ( reply , DBUS_TYPE_STRING , & introspection , DBUS_TYPE_INVALID ) ) {
free ( introspection ) ;
goto oom ;
}
free ( introspection ) ;
if ( ! dbus_connection_send ( connection , reply , NULL ) )
goto oom ;
return DBUS_HANDLER_RESULT_HANDLED ;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED ;
}
2012-10-03 01:07:00 +04:00
r = manager_get_job_from_dbus_path ( m , dbus_message_get_path ( message ) , & j ) ;
if ( r = = - ENOMEM )
goto oom ;
if ( r = = - ENOENT ) {
DBusError e ;
2010-02-02 14:42:08 +03:00
2012-10-03 01:07:00 +04:00
dbus_error_init ( & e ) ;
dbus_set_error_const ( & e , DBUS_ERROR_UNKNOWN_OBJECT , " Unknown job " ) ;
return bus_send_error_reply ( connection , message , & e , r ) ;
2010-02-02 14:42:08 +03:00
}
2012-10-03 01:07:00 +04:00
if ( r < 0 )
return bus_send_error_reply ( connection , message , NULL , r ) ;
2010-02-02 14:42:08 +03:00
2010-06-19 05:04:04 +04:00
return bus_job_message_dispatch ( j , connection , message ) ;
2010-10-13 05:03:31 +04:00
oom :
return DBUS_HANDLER_RESULT_NEED_MEMORY ;
2010-02-01 05:33:24 +03:00
}
const DBusObjectPathVTable bus_job_vtable = {
. message_function = bus_job_message_handler
} ;
2010-02-05 02:38:41 +03:00
2012-04-20 14:28:31 +04:00
static int job_send_message ( Job * j , DBusMessage * ( * new_message ) ( Job * j ) ) {
DBusMessage * m = NULL ;
2010-07-05 02:58:07 +04:00
int r ;
assert ( j ) ;
2012-04-20 14:28:31 +04:00
assert ( new_message ) ;
2010-07-05 02:58:07 +04:00
2012-04-23 03:24:04 +04:00
if ( bus_has_subscriber ( j - > manager ) | | j - > forgot_bus_clients ) {
2012-04-20 14:28:31 +04:00
m = new_message ( j ) ;
if ( ! m )
goto oom ;
r = bus_broadcast ( j - > manager , m ) ;
dbus_message_unref ( m ) ;
if ( r < 0 )
2010-07-05 02:58:07 +04:00
return r ;
2012-04-20 14:28:31 +04:00
} else {
2010-07-05 02:58:07 +04:00
/* If nobody is subscribed, we just send the message
2012-04-20 14:28:31 +04:00
* to the client ( s ) which created the job */
JobBusClient * cl ;
assert ( j - > bus_client_list ) ;
LIST_FOREACH ( client , cl , j - > bus_client_list ) {
assert ( cl - > bus ) ;
m = new_message ( j ) ;
if ( ! m )
goto oom ;
2010-07-05 02:58:07 +04:00
2012-04-20 14:28:31 +04:00
if ( ! dbus_message_set_destination ( m , cl - > name ) )
goto oom ;
2010-07-05 02:58:07 +04:00
2012-04-20 14:28:31 +04:00
if ( ! dbus_connection_send ( cl - > bus , m , NULL ) )
goto oom ;
2010-07-05 02:58:07 +04:00
2012-04-20 14:28:31 +04:00
dbus_message_unref ( m ) ;
m = NULL ;
}
2010-07-05 02:58:07 +04:00
}
return 0 ;
2012-04-20 14:28:31 +04:00
oom :
if ( m )
dbus_message_unref ( m ) ;
return - ENOMEM ;
2010-07-05 02:58:07 +04:00
}
2012-04-20 14:28:31 +04:00
static DBusMessage * new_change_signal_message ( Job * j ) {
2010-02-05 02:38:41 +03:00
DBusMessage * m = NULL ;
2012-04-20 14:28:31 +04:00
char * p = NULL ;
2010-02-05 02:38:41 +03:00
2012-04-20 14:28:31 +04:00
p = job_dbus_path ( j ) ;
if ( ! p )
2010-02-05 02:38:41 +03:00
goto oom ;
if ( j - > sent_dbus_new_signal ) {
2010-08-20 04:26:05 +04:00
/* Send a properties changed signal */
2012-04-20 14:28:31 +04:00
m = bus_properties_changed_new ( p , " org.freedesktop.systemd1.Job " , INVALIDATING_PROPERTIES ) ;
if ( ! m )
2010-02-05 02:38:41 +03:00
goto oom ;
2010-08-20 04:26:05 +04:00
2010-02-05 02:38:41 +03:00
} else {
/* Send a new signal */
2012-04-20 14:28:31 +04:00
m = dbus_message_new_signal ( " /org/freedesktop/systemd1 " , " org.freedesktop.systemd1.Manager " , " JobNew " ) ;
if ( ! m )
2010-02-05 02:38:41 +03:00
goto oom ;
if ( ! dbus_message_append_args ( m ,
DBUS_TYPE_UINT32 , & j - > id ,
DBUS_TYPE_OBJECT_PATH , & p ,
2012-05-04 00:53:25 +04:00
DBUS_TYPE_STRING , & j - > unit - > id ,
2010-02-05 02:38:41 +03:00
DBUS_TYPE_INVALID ) )
goto oom ;
}
2012-04-20 14:28:31 +04:00
return m ;
2010-02-05 02:38:41 +03:00
oom :
if ( m )
dbus_message_unref ( m ) ;
2012-04-20 14:28:31 +04:00
free ( p ) ;
return NULL ;
2010-02-05 02:38:41 +03:00
}
2012-04-20 14:28:31 +04:00
static DBusMessage * new_removed_signal_message ( Job * j ) {
2010-02-05 02:38:41 +03:00
DBusMessage * m = NULL ;
2012-04-20 14:28:31 +04:00
char * p = NULL ;
2011-02-24 04:36:34 +03:00
const char * r ;
2010-02-05 02:38:41 +03:00
2012-04-20 14:28:31 +04:00
p = job_dbus_path ( j ) ;
if ( ! p )
2010-02-05 02:38:41 +03:00
goto oom ;
2012-04-20 14:28:31 +04:00
m = dbus_message_new_signal ( " /org/freedesktop/systemd1 " , " org.freedesktop.systemd1.Manager " , " JobRemoved " ) ;
if ( ! m )
2010-02-05 02:38:41 +03:00
goto oom ;
2011-02-24 04:36:34 +03:00
r = job_result_to_string ( j - > result ) ;
2010-02-05 02:38:41 +03:00
if ( ! dbus_message_append_args ( m ,
DBUS_TYPE_UINT32 , & j - > id ,
DBUS_TYPE_OBJECT_PATH , & p ,
2012-05-04 00:53:25 +04:00
DBUS_TYPE_STRING , & j - > unit - > id ,
2011-02-24 04:36:34 +03:00
DBUS_TYPE_STRING , & r ,
2010-02-05 02:38:41 +03:00
DBUS_TYPE_INVALID ) )
goto oom ;
2012-04-20 14:28:31 +04:00
return m ;
2010-02-05 02:38:41 +03:00
2012-04-20 14:28:31 +04:00
oom :
if ( m )
dbus_message_unref ( m ) ;
2010-02-05 02:38:41 +03:00
free ( p ) ;
2012-04-20 14:28:31 +04:00
return NULL ;
}
void bus_job_send_change_signal ( Job * j ) {
assert ( j ) ;
if ( j - > in_dbus_queue ) {
LIST_REMOVE ( Job , dbus_queue , j - > manager - > dbus_job_queue , j ) ;
j - > in_dbus_queue = false ;
}
2012-04-23 03:24:04 +04:00
if ( ! bus_has_subscriber ( j - > manager ) & & ! j - > bus_client_list & & ! j - > forgot_bus_clients ) {
2012-04-20 14:28:31 +04:00
j - > sent_dbus_new_signal = true ;
return ;
}
if ( job_send_message ( j , new_change_signal_message ) < 0 )
goto oom ;
j - > sent_dbus_new_signal = true ;
2010-02-05 02:38:41 +03:00
return ;
oom :
2012-04-20 14:28:31 +04:00
log_error ( " Failed to allocate job change signal. " ) ;
}
2010-02-05 02:38:41 +03:00
2012-04-20 14:28:31 +04:00
void bus_job_send_removed_signal ( Job * j ) {
assert ( j ) ;
2010-02-05 02:38:41 +03:00
2012-04-23 03:24:04 +04:00
if ( ! bus_has_subscriber ( j - > manager ) & & ! j - > bus_client_list & & ! j - > forgot_bus_clients )
2012-04-20 14:28:31 +04:00
return ;
if ( ! j - > sent_dbus_new_signal )
bus_job_send_change_signal ( j ) ;
if ( job_send_message ( j , new_removed_signal_message ) < 0 )
goto oom ;
return ;
oom :
2010-02-05 02:38:41 +03:00
log_error ( " Failed to allocate job remove signal. " ) ;
}