2015-04-28 04:22:58 +03:00
/*
* Copyright ( C ) 2015 Red Hat , Inc .
*
* This program 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 .
*
* This program 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 this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include "config.h"
2015-08-24 22:44:40 +03:00
# include "rpmostreed-daemon.h"
# include "rpmostreed-sysroot.h"
# include "rpmostreed-types.h"
# include "rpmostreed-utils.h"
2015-04-28 04:22:58 +03:00
2015-06-10 01:57:32 +03:00
# include <libglnx.h>
2017-03-06 05:53:45 +03:00
# include <systemd/sd-journal.h>
2017-03-06 18:09:29 +03:00
# include <systemd/sd-daemon.h>
2017-02-04 14:27:05 +03:00
# include <stdio.h>
2015-04-28 04:22:58 +03:00
2017-03-08 00:16:30 +03:00
# define RPMOSTREE_MESSAGE_TRANSACTION_STARTED SD_ID128_MAKE(d5,be,a3,7a,8f,c8,4f,f5,9d,bc,fd,79,17,7b,7d,f8)
daemon: bump idle exit timeout to 60s
Since it usually takes more than 10s for users to enter consecutive
commands, let's bump the timeout to 60s. That way, we avoid the churn of
starting up twice and e.g. polluting the journal.
On a user interface level, this doesn't make a big difference: a
`status` from cold takes around 100ms, whereas with the daemon running,
it takes slightly less than 50ms. Slightly noticeable, but a non-issue.
However, auto-update will require some more work at startup, and a cold
`status` will bump to about 350ms, which is definitely more noticeable.
Bumping the timeout will ensure that at least within the span of one
"interaction" (multiple commands), we only do this work once.
Closes: #1192
Approved by: cgwalters
2018-01-10 20:16:46 +03:00
2017-12-15 20:01:46 +03:00
# define RPMOSTREED_CONF SYSCONFDIR " / rpm-ostreed.conf"
# define DAEMON_CONFIG_GROUP "Daemon"
2015-04-28 04:22:58 +03:00
/**
2015-08-24 22:44:40 +03:00
* SECTION : daemon
* @ title : RpmostreedDaemon
2015-04-28 04:22:58 +03:00
* @ short_description : Main daemon object
*
* Object holding all global state .
*/
2015-08-24 22:44:40 +03:00
typedef struct _RpmostreedDaemonClass RpmostreedDaemonClass ;
2015-04-28 04:22:58 +03:00
/**
2015-08-24 22:44:40 +03:00
* RpmostreedDaemon :
2015-04-28 04:22:58 +03:00
*
2015-08-24 22:44:40 +03:00
* The # RpmostreedDaemon structure contains only private data and should
* only be accessed using the provided API .
2015-04-28 04:22:58 +03:00
*/
2015-08-24 22:44:40 +03:00
struct _RpmostreedDaemon {
2015-04-28 04:22:58 +03:00
GObject parent_instance ;
2017-06-20 23:30:34 +03:00
GHashTable * bus_clients ; /* <utf8 busname, struct RpmOstreeClient> */
2017-03-06 05:53:45 +03:00
2017-03-07 19:47:38 +03:00
gboolean running ;
2017-06-20 23:30:34 +03:00
GDBusProxy * bus_proxy ;
2017-03-07 21:25:42 +03:00
GSource * idle_exit_source ;
guint rerender_status_id ;
2015-08-24 22:44:40 +03:00
RpmostreedSysroot * sysroot ;
2015-04-28 04:22:58 +03:00
gchar * sysroot_path ;
2018-01-12 20:34:29 +03:00
/* we only have one setting for now, so let's just keep it in the main struct */
guint idle_exit_timeout ;
2015-04-28 04:22:58 +03:00
GDBusConnection * connection ;
GDBusObjectManagerServer * object_manager ;
} ;
2015-08-24 22:44:40 +03:00
struct _RpmostreedDaemonClass {
2015-04-28 04:22:58 +03:00
GObjectClass parent_class ;
} ;
2015-08-24 22:44:40 +03:00
static RpmostreedDaemon * _daemon_instance ;
2015-04-28 04:22:58 +03:00
enum
{
PROP_0 ,
PROP_CONNECTION ,
PROP_OBJECT_MANAGER ,
2015-09-01 21:43:03 +03:00
PROP_SYSROOT_PATH
2015-04-28 04:22:58 +03:00
} ;
2015-08-24 22:44:40 +03:00
static void rpmostreed_daemon_initable_iface_init ( GInitableIface * iface ) ;
2017-03-07 21:25:42 +03:00
static void update_status ( RpmostreedDaemon * self ) ;
2015-07-15 17:33:45 +03:00
2015-08-24 22:44:40 +03:00
G_DEFINE_TYPE_WITH_CODE ( RpmostreedDaemon , rpmostreed_daemon , G_TYPE_OBJECT ,
2015-07-15 17:33:45 +03:00
G_IMPLEMENT_INTERFACE ( G_TYPE_INITABLE ,
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_initable_iface_init ) )
2015-04-28 04:22:58 +03:00
2017-06-20 23:30:34 +03:00
struct RpmOstreeClient {
char * address ;
guint name_watch_id ;
gboolean uid_valid ;
uid_t uid ;
} ;
static void
rpmostree_client_free ( struct RpmOstreeClient * client )
{
g_free ( client - > address ) ;
g_free ( client ) ;
}
static char *
rpmostree_client_to_string ( struct RpmOstreeClient * client )
{
g_autoptr ( GString ) buf = g_string_new ( " client " ) ;
g_string_append ( buf , client - > address ) ;
if ( client - > uid_valid )
g_string_append_printf ( buf , " (uid %lu) " , ( unsigned long ) client - > uid ) ;
else
g_string_append ( buf , " (unknown uid) " ) ;
return g_string_free ( g_steal_pointer ( & buf ) , FALSE ) ;
}
2015-04-28 04:22:58 +03:00
typedef struct {
GAsyncReadyCallback callback ;
gpointer callback_data ;
gpointer proxy_source ;
} DaemonTaskData ;
static void
daemon_finalize ( GObject * object )
{
2015-08-24 22:44:40 +03:00
RpmostreedDaemon * self = RPMOSTREED_DAEMON ( object ) ;
2015-04-28 04:22:58 +03:00
g_clear_object ( & self - > object_manager ) ;
self - > object_manager = NULL ;
2015-05-25 20:41:27 +03:00
g_clear_object ( & self - > sysroot ) ;
2017-06-20 23:30:34 +03:00
g_clear_object ( & self - > bus_proxy ) ;
2015-05-25 20:41:27 +03:00
2015-04-28 04:22:58 +03:00
g_object_unref ( self - > connection ) ;
2017-03-06 05:53:45 +03:00
g_hash_table_unref ( self - > bus_clients ) ;
2017-03-07 21:25:42 +03:00
g_clear_pointer ( & self - > idle_exit_source , ( GDestroyNotify ) g_source_unref ) ;
if ( self - > rerender_status_id > 0 )
g_source_remove ( self - > rerender_status_id ) ;
2015-04-28 04:22:58 +03:00
g_free ( self - > sysroot_path ) ;
2015-08-24 22:44:40 +03:00
G_OBJECT_CLASS ( rpmostreed_daemon_parent_class ) - > finalize ( object ) ;
2015-04-28 04:22:58 +03:00
_daemon_instance = NULL ;
}
static void
daemon_get_property ( GObject * object ,
guint prop_id ,
GValue * value ,
GParamSpec * pspec )
{
2015-08-24 22:44:40 +03:00
RpmostreedDaemon * self = RPMOSTREED_DAEMON ( object ) ;
2015-04-28 04:22:58 +03:00
switch ( prop_id )
{
case PROP_OBJECT_MANAGER :
g_value_set_object ( value , self - > object_manager ) ;
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static void
daemon_set_property ( GObject * object ,
guint prop_id ,
const GValue * value ,
GParamSpec * pspec )
{
2015-08-24 22:44:40 +03:00
RpmostreedDaemon * self = RPMOSTREED_DAEMON ( object ) ;
2015-04-28 04:22:58 +03:00
switch ( prop_id )
{
case PROP_CONNECTION :
g_assert ( self - > connection = = NULL ) ;
self - > connection = g_value_dup_object ( value ) ;
break ;
case PROP_SYSROOT_PATH :
g_assert ( self - > sysroot_path = = NULL ) ;
self - > sysroot_path = g_value_dup_string ( value ) ;
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID ( object , prop_id , pspec ) ;
break ;
}
}
static void
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_init ( RpmostreedDaemon * self )
2015-04-28 04:22:58 +03:00
{
g_assert ( _daemon_instance = = NULL ) ;
_daemon_instance = self ;
self - > sysroot_path = NULL ;
2015-05-25 20:41:27 +03:00
self - > sysroot = NULL ;
2017-06-20 23:30:34 +03:00
self - > bus_clients = g_hash_table_new_full ( g_str_hash , g_str_equal , NULL , ( GDestroyNotify ) rpmostree_client_free ) ;
2015-04-28 04:22:58 +03:00
}
2017-02-04 14:27:05 +03:00
static void
on_active_txn_changed ( GObject * object ,
GParamSpec * pspec ,
gpointer user_data )
{
RpmostreedDaemon * self = user_data ;
2017-03-08 00:16:30 +03:00
g_autoptr ( GVariant ) active_txn = NULL ;
const char * method , * sender , * path ;
g_object_get ( rpmostreed_sysroot_get ( ) , " active-transaction " , & active_txn , NULL ) ;
if ( active_txn )
{
g_variant_get ( active_txn , " (&s&s&s) " , & method , & sender , & path ) ;
if ( * method )
{
2017-06-20 23:30:34 +03:00
g_autofree char * client_str = rpmostreed_daemon_client_get_string ( self , sender ) ;
struct RpmOstreeClient * clientdata = g_hash_table_lookup ( self - > bus_clients , sender ) ;
g_autofree char * client_data_msg = NULL ;
if ( clientdata & & clientdata - > uid_valid )
client_data_msg = g_strdup_printf ( " CLIENT_UID=%u " , clientdata - > uid ) ;
2017-03-08 00:16:30 +03:00
sd_journal_send ( " MESSAGE_ID= " SD_ID128_FORMAT_STR , SD_ID128_FORMAT_VAL ( RPMOSTREE_MESSAGE_TRANSACTION_STARTED ) ,
2017-06-20 23:30:34 +03:00
" MESSAGE=Initiated txn %s for %s: %s " , method , client_str , path ,
" BUS_ADDRESS=%s " , sender ,
client_data_msg ? : NULL ,
2017-03-08 00:16:30 +03:00
NULL ) ;
}
}
2017-03-07 21:25:42 +03:00
update_status ( self ) ;
2017-02-04 14:27:05 +03:00
}
2015-07-15 17:33:45 +03:00
static gboolean
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_initable_init ( GInitable * initable ,
GCancellable * cancellable ,
GError * * error )
2015-04-28 04:22:58 +03:00
{
2015-08-24 22:44:40 +03:00
RpmostreedDaemon * self = RPMOSTREED_DAEMON ( initable ) ;
2015-04-28 04:22:58 +03:00
self - > object_manager = g_dbus_object_manager_server_new ( BASE_DBUS_PATH ) ;
2017-07-07 20:45:57 +03:00
2015-04-28 04:22:58 +03:00
/* Export the ObjectManager */
g_dbus_object_manager_server_set_connection ( self - > object_manager , self - > connection ) ;
g_debug ( " exported object manager " ) ;
2017-12-15 20:01:46 +03:00
/* do this early so sysroot startup sets properties to the right values */
if ( ! rpmostreed_daemon_reload_config ( self , NULL , error ) )
return FALSE ;
2017-07-07 20:45:57 +03:00
g_autofree gchar * path =
rpmostreed_generate_object_path ( BASE_DBUS_PATH , " Sysroot " , NULL ) ;
2015-08-24 22:44:40 +03:00
self - > sysroot = g_object_new ( RPMOSTREED_TYPE_SYSROOT ,
2015-09-02 19:40:50 +03:00
" path " , self - > sysroot_path ,
2015-05-25 20:41:27 +03:00
NULL ) ;
2016-03-04 19:34:10 +03:00
if ( ! rpmostreed_sysroot_populate ( rpmostreed_sysroot_get ( ) , cancellable , error ) )
2017-07-07 20:45:57 +03:00
return glnx_prefix_error ( error , " Error setting up sysroot " ) ;
2015-05-25 20:41:27 +03:00
2017-02-04 14:27:05 +03:00
g_signal_connect ( rpmostreed_sysroot_get ( ) , " notify::active-transaction " ,
G_CALLBACK ( on_active_txn_changed ) , self ) ;
2017-06-20 23:30:34 +03:00
self - > bus_proxy =
g_dbus_proxy_new_sync ( self - > connection ,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS ,
NULL ,
" org.freedesktop.DBus " ,
" /org/freedesktop/DBus " ,
" org.freedesktop.DBus " ,
NULL ,
error ) ;
if ( ! self - > bus_proxy )
2017-07-07 20:45:57 +03:00
return FALSE ;
2017-06-20 23:30:34 +03:00
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_publish ( self , path , FALSE , self - > sysroot ) ;
2015-06-11 05:17:49 +03:00
g_dbus_connection_start_message_processing ( self - > connection ) ;
2015-04-28 04:22:58 +03:00
g_debug ( " daemon constructed " ) ;
2015-05-25 20:41:27 +03:00
2017-07-07 20:45:57 +03:00
return TRUE ;
2015-04-28 04:22:58 +03:00
}
2017-12-15 20:01:46 +03:00
/* Returns TRUE if config file exists and could be loaded or if config file doesn't exist.
* Returns FALSE if config file exists but could not be loaded . */
static gboolean
maybe_load_config_keyfile ( GKeyFile * * out_keyfile ,
GError * * error )
{
g_autoptr ( GError ) local_error = NULL ;
g_autoptr ( GKeyFile ) keyfile = g_key_file_new ( ) ;
if ( g_key_file_load_from_file ( keyfile , RPMOSTREED_CONF , 0 , & local_error ) )
{
* out_keyfile = g_steal_pointer ( & keyfile ) ;
sd_journal_print ( LOG_INFO , " Reading config file '%s' " , RPMOSTREED_CONF ) ;
return TRUE ;
}
if ( ! g_error_matches ( local_error , G_FILE_ERROR , G_FILE_ERROR_NOENT ) )
{
g_propagate_error ( error , g_steal_pointer ( & local_error ) ) ;
return FALSE ;
}
sd_journal_print ( LOG_WARNING , " Missing config file '%s'; using compiled defaults " ,
RPMOSTREED_CONF ) ;
return TRUE ;
}
2018-01-12 20:34:29 +03:00
static guint64
get_config_uint64 ( GKeyFile * keyfile ,
const char * key ,
guint64 default_val )
{
if ( keyfile & & g_key_file_has_key ( keyfile , DAEMON_CONFIG_GROUP , key , NULL ) )
{
g_autoptr ( GError ) local_error = NULL ;
guint64 r = g_key_file_get_uint64 ( keyfile , DAEMON_CONFIG_GROUP , key , & local_error ) ;
if ( ! local_error )
return r ;
2018-01-18 20:39:19 +03:00
if ( g_error_matches ( local_error , G_KEY_FILE_ERROR , G_KEY_FILE_ERROR_INVALID_VALUE ) )
sd_journal_print ( LOG_WARNING , " Bad uint64 for '%s': %s; using compiled defaults " ,
key , local_error - > message ) ;
2018-01-12 20:34:29 +03:00
}
return default_val ;
}
2017-12-15 20:01:46 +03:00
gboolean
rpmostreed_daemon_reload_config ( RpmostreedDaemon * self ,
gboolean * out_changed ,
GError * * error )
{
g_autoptr ( GKeyFile ) config = NULL ;
if ( ! maybe_load_config_keyfile ( & config , error ) )
return FALSE ;
2018-01-12 20:34:29 +03:00
/* default to 60s by default; our startup is non-trivial so staying around will ensure
* follow - up requests are more responsive */
guint64 idle_exit_timeout = get_config_uint64 ( config , " IdleExitTimeout " , 60 ) ;
/* don't update changed for this; it's contained to RpmostreedDaemon so no other objects
* need to be reloaded if it changes */
self - > idle_exit_timeout = idle_exit_timeout ;
2017-12-15 20:01:46 +03:00
if ( out_changed )
* out_changed = FALSE ;
return TRUE ;
}
2015-04-28 04:22:58 +03:00
static void
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_class_init ( RpmostreedDaemonClass * klass )
2015-04-28 04:22:58 +03:00
{
GObjectClass * gobject_class ;
gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > finalize = daemon_finalize ;
gobject_class - > set_property = daemon_set_property ;
gobject_class - > get_property = daemon_get_property ;
/**
* Daemon : connection :
*
* The # GDBusConnection the daemon is for .
*/
g_object_class_install_property ( gobject_class ,
PROP_CONNECTION ,
g_param_spec_object ( " connection " ,
" Connection " ,
" The D-Bus connection the daemon is for " ,
G_TYPE_DBUS_CONNECTION ,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS ) ) ;
/**
* Daemon : object - manager :
*
* The # GDBusObjectManager used by the daemon
*/
g_object_class_install_property ( gobject_class ,
PROP_OBJECT_MANAGER ,
g_param_spec_object ( " object-manager " ,
" Object Manager " ,
" The D-Bus Object Manager server used by the daemon " ,
G_TYPE_DBUS_OBJECT_MANAGER_SERVER ,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS ) ) ;
g_object_class_install_property ( gobject_class ,
PROP_SYSROOT_PATH ,
g_param_spec_string ( " sysroot-path " ,
2015-08-24 22:44:40 +03:00
" Sysroot Path " ,
" Sysroot location on the filesystem " ,
FALSE ,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS ) ) ;
2015-04-28 04:22:58 +03:00
}
2015-07-15 17:33:45 +03:00
static void
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_initable_iface_init ( GInitableIface * iface )
2015-07-15 17:33:45 +03:00
{
2015-08-24 22:44:40 +03:00
iface - > init = rpmostreed_daemon_initable_init ;
2015-07-15 17:33:45 +03:00
}
2015-04-28 04:22:58 +03:00
/**
2015-08-24 22:44:40 +03:00
* rpmostreed_daemon_get :
2015-04-28 04:22:58 +03:00
*
2015-08-24 22:44:40 +03:00
* Returns : ( transfer none ) : The singleton # RpmostreedDaemon instance
2015-04-28 04:22:58 +03:00
*/
2015-08-24 22:44:40 +03:00
RpmostreedDaemon *
rpmostreed_daemon_get ( void )
2015-04-28 04:22:58 +03:00
{
g_assert ( _daemon_instance ) ;
return _daemon_instance ;
}
2017-03-06 05:53:45 +03:00
static void
on_name_owner_changed ( GDBusConnection * connection ,
const gchar * sender_name ,
const gchar * object_path ,
const gchar * interface_name ,
const gchar * signal_name ,
GVariant * parameters ,
gpointer user_data )
{
RpmostreedDaemon * self = user_data ;
const char * name ;
const char * old_owner ;
gboolean has_old_owner ;
const char * new_owner ;
gboolean has_new_owner ;
if ( g_strcmp0 ( object_path , " /org/freedesktop/DBus " ) ! = 0 | |
g_strcmp0 ( interface_name , " org.freedesktop.DBus " ) ! = 0 | |
g_strcmp0 ( sender_name , " org.freedesktop.DBus " ) ! = 0 )
return ;
g_variant_get ( parameters , " (&s&s&s) " , & name , & old_owner , & new_owner ) ;
has_old_owner = old_owner & & * old_owner ;
has_new_owner = new_owner & & * new_owner ;
if ( has_old_owner & & ! has_new_owner )
rpmostreed_daemon_remove_client ( self , name ) ;
}
2017-03-07 21:25:42 +03:00
static gboolean
idle_update_status ( void * data )
{
RpmostreedDaemon * self = data ;
update_status ( self ) ;
return TRUE ;
}
static gboolean
on_idle_exit ( void * data )
{
RpmostreedDaemon * self = data ;
g_clear_pointer ( & self - > idle_exit_source , ( GDestroyNotify ) g_source_unref ) ;
sd_notifyf ( 0 , " STATUS=Exiting due to idle " ) ;
self - > running = FALSE ;
g_main_context_wakeup ( NULL ) ;
return FALSE ;
}
2017-03-06 18:09:29 +03:00
static void
2017-03-07 21:25:42 +03:00
update_status ( RpmostreedDaemon * self )
2017-03-06 18:09:29 +03:00
{
2017-02-04 14:27:05 +03:00
g_autoptr ( GVariant ) active_txn = NULL ;
gboolean have_active_txn = FALSE ;
const char * method , * sender , * path ;
2017-03-07 21:25:42 +03:00
const guint n_clients = g_hash_table_size ( self - > bus_clients ) ;
2017-10-12 17:07:45 +03:00
gboolean currently_idle = FALSE ;
2017-02-04 14:27:05 +03:00
g_object_get ( rpmostreed_sysroot_get ( ) , " active-transaction " , & active_txn , NULL ) ;
if ( active_txn )
{
g_variant_get ( active_txn , " (&s&s&s) " , & method , & sender , & path ) ;
if ( * method )
have_active_txn = TRUE ;
}
2018-01-12 20:34:29 +03:00
if ( ! getenv ( " RPMOSTREE_DEBUG_DISABLE_DAEMON_IDLE_EXIT " ) & & self - > idle_exit_timeout > 0 )
2017-10-12 17:07:45 +03:00
currently_idle = ! have_active_txn & & n_clients = = 0 ;
2017-03-07 21:25:42 +03:00
if ( currently_idle & & ! self - > idle_exit_source )
{
/* I think adding some randomness is a good idea, to mitigate
* pathological cases where someone is talking to us at the same
* frequency as our exit timer . */
2018-01-12 20:34:29 +03:00
const guint idle_exit_secs = self - > idle_exit_timeout + g_random_int_range ( 0 , 5 ) ;
2017-03-07 21:25:42 +03:00
self - > idle_exit_source = g_timeout_source_new_seconds ( idle_exit_secs ) ;
g_source_set_callback ( self - > idle_exit_source , on_idle_exit , self , NULL ) ;
g_source_attach ( self - > idle_exit_source , NULL ) ;
/* This source ensures we update the systemd status to show admins
* when we may auto - exit .
*/
self - > rerender_status_id = g_timeout_add_seconds ( 1 , idle_update_status , self ) ;
sd_journal_print ( LOG_INFO , " In idle state; will auto-exit in %u seconds " , idle_exit_secs ) ;
}
else if ( ! currently_idle & & self - > idle_exit_source )
{
g_source_destroy ( self - > idle_exit_source ) ;
g_clear_pointer ( & self - > idle_exit_source , ( GDestroyNotify ) g_source_unref ) ;
g_source_remove ( self - > rerender_status_id ) ;
}
2017-02-04 14:27:05 +03:00
if ( have_active_txn )
2017-03-08 00:16:30 +03:00
{
2017-06-20 23:37:37 +03:00
sd_notifyf ( 0 , " STATUS=clients=%u; txn=%s caller=%s path=%s " , g_hash_table_size ( self - > bus_clients ) ,
method , sender , path ) ;
2017-03-08 00:16:30 +03:00
}
2017-03-07 21:25:42 +03:00
else if ( n_clients > 0 )
sd_notifyf ( 0 , " STATUS=clients=%u; idle " , n_clients ) ;
2017-10-12 17:07:45 +03:00
else if ( currently_idle )
2017-03-07 21:25:42 +03:00
{
guint64 readytime = g_source_get_ready_time ( self - > idle_exit_source ) ;
guint64 curtime = g_source_get_time ( self - > idle_exit_source ) ;
guint64 timeout_micros = readytime - curtime ;
2017-10-04 23:29:35 +03:00
if ( readytime < curtime )
2017-03-07 21:25:42 +03:00
timeout_micros = 0 ;
g_assert ( currently_idle & & self - > idle_exit_source ) ;
sd_notifyf ( 0 , " STATUS=clients=%u; idle exit in % " G_GUINT64_FORMAT " seconds " , n_clients ,
timeout_micros / G_USEC_PER_SEC ) ;
}
2017-03-06 18:09:29 +03:00
}
2017-07-25 21:24:35 +03:00
gboolean
rpmostreed_get_client_uid ( RpmostreedDaemon * self ,
const char * client ,
uid_t * out_uid )
2017-03-06 05:53:45 +03:00
{
2017-06-20 23:30:34 +03:00
g_autoptr ( GError ) local_error = NULL ;
g_autoptr ( GVariant ) uidcall = g_dbus_proxy_call_sync ( self - > bus_proxy ,
" GetConnectionUnixUser " ,
g_variant_new ( " (s) " , client ) ,
G_DBUS_CALL_FLAGS_NONE ,
2000 , NULL , & local_error ) ;
if ( ! uidcall )
{
sd_journal_print ( LOG_WARNING , " Failed to GetConnectionUnixUser for client %s: %s " ,
client , local_error - > message ) ;
2017-07-25 21:24:35 +03:00
return FALSE ;
2017-06-20 23:30:34 +03:00
}
2017-07-25 21:24:35 +03:00
g_variant_get ( uidcall , " (u) " , out_uid ) ;
return TRUE ;
}
void
rpmostreed_daemon_add_client ( RpmostreedDaemon * self ,
const char * client )
{
if ( g_hash_table_lookup ( self - > bus_clients , client ) )
return ;
2017-06-20 23:30:34 +03:00
struct RpmOstreeClient * clientdata = g_new0 ( struct RpmOstreeClient , 1 ) ;
clientdata - > address = g_strdup ( client ) ;
clientdata - > name_watch_id =
2017-06-02 19:40:19 +03:00
g_dbus_connection_signal_subscribe ( self - > connection ,
" org.freedesktop.DBus " ,
" org.freedesktop.DBus " ,
" NameOwnerChanged " ,
" /org/freedesktop/DBus " ,
client ,
G_DBUS_SIGNAL_FLAGS_NONE ,
on_name_owner_changed ,
g_object_ref ( self ) ,
g_object_unref ) ;
2017-07-25 21:24:35 +03:00
if ( rpmostreed_get_client_uid ( self , client , & clientdata - > uid ) )
clientdata - > uid_valid = TRUE ;
2017-06-02 19:40:19 +03:00
2017-06-20 23:30:34 +03:00
g_hash_table_insert ( self - > bus_clients , ( char * ) clientdata - > address , clientdata ) ;
g_autofree char * clientstr = rpmostree_client_to_string ( clientdata ) ;
sd_journal_print ( LOG_INFO , " %s added; new total=%u " , clientstr , g_hash_table_size ( self - > bus_clients ) ) ;
2017-03-07 21:25:42 +03:00
update_status ( self ) ;
2017-03-06 05:53:45 +03:00
}
2017-06-20 23:30:34 +03:00
/* Returns a string representing the state of the bus name @client.
* If @ client is unknown ( i . e . has not called RegisterClient ) , we just
* return " caller " .
*/
char *
rpmostreed_daemon_client_get_string ( RpmostreedDaemon * self , const char * client )
2017-06-20 23:45:13 +03:00
{
2017-06-20 23:30:34 +03:00
struct RpmOstreeClient * clientdata = g_hash_table_lookup ( self - > bus_clients , client ) ;
if ( ! clientdata )
return g_strdup_printf ( " caller %s " , client ) ;
else
return rpmostree_client_to_string ( clientdata ) ;
2017-06-20 23:45:13 +03:00
}
2017-03-06 05:53:45 +03:00
void
rpmostreed_daemon_remove_client ( RpmostreedDaemon * self ,
const char * client )
{
2017-06-20 23:30:34 +03:00
gpointer origkey , clientdatap ;
struct RpmOstreeClient * clientdata ;
if ( ! g_hash_table_lookup_extended ( self - > bus_clients , client , & origkey , & clientdatap ) )
2017-03-06 05:53:45 +03:00
return ;
2017-06-20 23:30:34 +03:00
clientdata = clientdatap ;
g_dbus_connection_signal_unsubscribe ( self - > connection , clientdata - > name_watch_id ) ;
g_autofree char * clientstr = rpmostree_client_to_string ( clientdata ) ;
2017-03-06 05:53:45 +03:00
g_hash_table_remove ( self - > bus_clients , client ) ;
2017-03-07 21:25:42 +03:00
const guint remaining = g_hash_table_size ( self - > bus_clients ) ;
sd_journal_print ( LOG_INFO , " %s vanished; remaining=%u " , clientstr , remaining ) ;
update_status ( self ) ;
2017-03-06 05:53:45 +03:00
}
2017-03-07 19:47:38 +03:00
void
rpmostreed_daemon_exit_now ( RpmostreedDaemon * self )
{
self - > running = FALSE ;
}
void
rpmostreed_daemon_run_until_idle_exit ( RpmostreedDaemon * self )
{
self - > running = TRUE ;
2017-03-07 21:25:42 +03:00
update_status ( self ) ;
2017-03-07 19:47:38 +03:00
while ( self - > running )
g_main_context_iteration ( NULL , TRUE ) ;
}
2015-05-25 20:21:06 +03:00
void
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_publish ( RpmostreedDaemon * self ,
const gchar * path ,
gboolean uniquely ,
gpointer thing )
2015-05-25 20:21:06 +03:00
{
GDBusInterface * prev = NULL ;
GDBusInterfaceInfo * info = NULL ;
GDBusObjectSkeleton * object = NULL ;
2017-03-23 04:44:09 +03:00
g_autoptr ( GDBusObjectSkeleton ) owned_object = NULL ;
2015-05-25 20:21:06 +03:00
2015-08-24 22:44:40 +03:00
g_return_if_fail ( RPMOSTREED_IS_DAEMON ( self ) ) ;
2015-05-25 20:21:06 +03:00
g_return_if_fail ( path ! = NULL ) ;
if ( G_IS_DBUS_INTERFACE ( thing ) )
{
g_debug ( " %spublishing iface: %s %s " , uniquely ? " uniquely " : " " , path ,
g_dbus_interface_get_info ( thing ) - > name ) ;
object = G_DBUS_OBJECT_SKELETON ( g_dbus_object_manager_get_object ( G_DBUS_OBJECT_MANAGER ( self - > object_manager ) , path ) ) ;
if ( object ! = NULL )
{
if ( uniquely )
{
info = g_dbus_interface_get_info ( thing ) ;
prev = g_dbus_object_get_interface ( G_DBUS_OBJECT ( object ) , info - > name ) ;
if ( prev )
{
g_object_unref ( prev ) ;
g_object_unref ( object ) ;
object = NULL ;
}
}
}
if ( object = = NULL )
2017-03-23 04:44:09 +03:00
object = owned_object = g_dbus_object_skeleton_new ( path ) ;
2015-05-25 20:21:06 +03:00
g_dbus_object_skeleton_add_interface ( object , thing ) ;
}
else
{
g_critical ( " Unsupported type to publish: %s " , G_OBJECT_TYPE_NAME ( thing ) ) ;
return ;
}
if ( uniquely )
g_dbus_object_manager_server_export_uniquely ( self - > object_manager , object ) ;
else
g_dbus_object_manager_server_export ( self - > object_manager , object ) ;
}
void
2015-08-24 22:44:40 +03:00
rpmostreed_daemon_unpublish ( RpmostreedDaemon * self ,
const gchar * path ,
gpointer thing )
2015-05-25 20:21:06 +03:00
{
GDBusObject * object ;
gboolean unexport = FALSE ;
GList * interfaces , * l ;
2015-08-24 22:44:40 +03:00
g_return_if_fail ( RPMOSTREED_IS_DAEMON ( self ) ) ;
2015-05-25 20:21:06 +03:00
g_return_if_fail ( path ! = NULL ) ;
if ( self - > object_manager = = NULL )
return ;
object = g_dbus_object_manager_get_object ( G_DBUS_OBJECT_MANAGER ( self - > object_manager ) , path ) ;
if ( object = = NULL )
return ;
path = g_dbus_object_get_object_path ( G_DBUS_OBJECT ( object ) ) ;
if ( G_IS_DBUS_INTERFACE ( thing ) )
{
g_debug ( " unpublishing interface: %s %s " , path ,
g_dbus_interface_get_info ( thing ) - > name ) ;
unexport = TRUE ;
interfaces = g_dbus_object_get_interfaces ( object ) ;
for ( l = interfaces ; l ! = NULL ; l = g_list_next ( l ) )
{
if ( G_DBUS_INTERFACE ( l - > data ) ! = G_DBUS_INTERFACE ( thing ) )
unexport = FALSE ;
}
g_list_free_full ( interfaces , g_object_unref ) ;
/*
* HACK : GDBusObjectManagerServer is broken . . . and sends InterfaceRemoved
* too many times , if you remove all interfaces manually , and then unexport
* a GDBusObject . So only do it here if we ' re not unexporting the object .
*/
if ( ! unexport )
g_dbus_object_skeleton_remove_interface ( G_DBUS_OBJECT_SKELETON ( object ) , thing ) ;
else
g_debug ( " (unpublishing object, too) " ) ;
}
else if ( thing = = NULL )
{
unexport = TRUE ;
}
else
{
g_critical ( " Unsupported type to unpublish: %s " , G_OBJECT_TYPE_NAME ( thing ) ) ;
}
if ( unexport )
g_dbus_object_manager_server_unexport ( self - > object_manager , path ) ;
g_object_unref ( object ) ;
}