libmalcontent: Add support for setting session limits

Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
Philip Withnall 2019-12-09 20:37:48 +00:00
parent 5e49cb7831
commit ee7ed7dc35
4 changed files with 569 additions and 0 deletions

View File

@ -1104,3 +1104,232 @@ mct_manager_get_session_limits_finish (MctManager *self,
return g_task_propagate_pointer (G_TASK (result), error); return g_task_propagate_pointer (G_TASK (result), error);
} }
/**
* mct_manager_set_session_limits:
* @self: a #MctManager
* @user_id: ID of the user to set the limits for, typically coming from getuid()
* @session_limits: (transfer none): the session limits to set for the user
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronous version of mct_manager_set_session_limits_async().
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: 0.5.0
*/
gboolean
mct_manager_set_session_limits (MctManager *self,
uid_t user_id,
MctSessionLimits *session_limits,
MctManagerSetValueFlags flags,
GCancellable *cancellable,
GError **error)
{
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) limit_variant = NULL;
const gchar *limit_property_name = NULL;
g_autoptr(GVariant) limit_type_variant = NULL;
g_autoptr(GVariant) limit_result_variant = NULL;
g_autoptr(GVariant) limit_type_result_variant = NULL;
g_autoptr(GError) local_error = NULL;
g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
g_return_val_if_fail (session_limits != NULL, FALSE);
g_return_val_if_fail (session_limits->ref_count >= 1, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
object_path = accounts_find_user_by_id (self->connection, user_id,
(flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE),
cancellable, error);
if (object_path == NULL)
return FALSE;
switch (session_limits->limit_type)
{
case MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE:
limit_variant = g_variant_new ("(uu)",
session_limits->daily_start_time,
session_limits->daily_end_time);
limit_property_name = "DailySchedule";
break;
case MCT_SESSION_LIMITS_TYPE_NONE:
limit_variant = NULL;
limit_property_name = NULL;
break;
default:
g_assert_not_reached ();
}
limit_type_variant = g_variant_new_uint32 (session_limits->limit_type);
if (limit_property_name != NULL)
{
/* Change the details of the new limit first, so that all the properties are
* correct by the time the limit type is changed over. */
limit_result_variant =
g_dbus_connection_call_sync (self->connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.SessionLimits",
limit_property_name,
g_steal_pointer (&limit_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_manager_error (local_error, user_id));
return FALSE;
}
}
limit_type_result_variant =
g_dbus_connection_call_sync (self->connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.SessionLimits",
"LimitType",
g_steal_pointer (&limit_type_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_manager_error (local_error, user_id));
return FALSE;
}
return TRUE;
}
static void set_session_limits_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable);
typedef struct
{
uid_t user_id;
MctSessionLimits *session_limits; /* (owned) */
MctManagerSetValueFlags flags;
} SetSessionLimitsData;
static void
set_session_limits_data_free (SetSessionLimitsData *data)
{
mct_session_limits_unref (data->session_limits);
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetSessionLimitsData, set_session_limits_data_free)
/**
* mct_manager_set_session_limits_async:
* @self: a #MctManager
* @user_id: ID of the user to set the limits for, typically coming from getuid()
* @session_limits: (transfer none): the session limits to set for the user
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: a #GAsyncReadyCallback
* @user_data: user data to pass to @callback
*
* Asynchronously set the session limits settings for the given @user_id to the
* given @session_limits instance.
*
* On failure, an #MctManagerError, a #GDBusError or a #GIOError will be
* returned via mct_manager_set_session_limits_finish(). The users session
* limits settings will be left in an undefined state.
*
* Since: 0.5.0
*/
void
mct_manager_set_session_limits_async (MctManager *self,
uid_t user_id,
MctSessionLimits *session_limits,
MctManagerSetValueFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(SetSessionLimitsData) data = NULL;
g_return_if_fail (MCT_IS_MANAGER (self));
g_return_if_fail (session_limits != NULL);
g_return_if_fail (session_limits->ref_count >= 1);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, mct_manager_set_session_limits_async);
data = g_new0 (SetSessionLimitsData, 1);
data->user_id = user_id;
data->session_limits = mct_session_limits_ref (session_limits);
data->flags = flags;
g_task_set_task_data (task, g_steal_pointer (&data),
(GDestroyNotify) set_session_limits_data_free);
g_task_run_in_thread (task, set_session_limits_thread_cb);
}
static void
set_session_limits_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
gboolean success;
MctManager *manager = MCT_MANAGER (source_object);
SetSessionLimitsData *data = task_data;
g_autoptr(GError) local_error = NULL;
success = mct_manager_set_session_limits (manager, data->user_id,
data->session_limits, data->flags,
cancellable, &local_error);
if (local_error != NULL)
g_task_return_error (task, g_steal_pointer (&local_error));
else
g_task_return_boolean (task, success);
}
/**
* mct_manager_set_session_limits_finish:
* @self: a #MctManager
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finish an asynchronous operation to set the session limits for a user,
* started with mct_manager_set_session_limits_async().
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: 0.5.0
*/
gboolean
mct_manager_set_session_limits_finish (MctManager *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}

View File

@ -151,4 +151,21 @@ MctSessionLimits *mct_manager_get_session_limits_finish (MctManager
GAsyncResult *result, GAsyncResult *result,
GError **error); GError **error);
gboolean mct_manager_set_session_limits (MctManager *self,
uid_t user_id,
MctSessionLimits *session_limits,
MctManagerSetValueFlags flags,
GCancellable *cancellable,
GError **error);
void mct_manager_set_session_limits_async (MctManager *self,
uid_t user_id,
MctSessionLimits *session_limits,
MctManagerSetValueFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mct_manager_set_session_limits_finish (MctManager *self,
GAsyncResult *result,
GError **error);
G_END_DECLS G_END_DECLS

View File

@ -189,3 +189,263 @@ out:
return user_allowed_now; return user_allowed_now;
} }
/*
* Actual implementation of #MctSessionLimitsBuilder.
*
* All members are %NULL if un-initialised, cleared, or ended.
*/
typedef struct
{
MctSessionLimitsType limit_type;
/* Which member is used is determined by @limit_type: */
union
{
struct
{
guint start_time; /* seconds since midnight */
guint end_time; /* seconds since midnight */
} daily_schedule;
};
/*< private >*/
gpointer padding[10];
} MctSessionLimitsBuilderReal;
G_STATIC_ASSERT (sizeof (MctSessionLimitsBuilderReal) ==
sizeof (MctSessionLimitsBuilder));
G_STATIC_ASSERT (__alignof__ (MctSessionLimitsBuilderReal) ==
__alignof__ (MctSessionLimitsBuilder));
G_DEFINE_BOXED_TYPE (MctSessionLimitsBuilder, mct_session_limits_builder,
mct_session_limits_builder_copy, mct_session_limits_builder_free)
/**
* mct_session_limits_builder_init:
* @builder: an uninitialised #MctSessionLimitsBuilder
*
* Initialise the given @builder so it can be used to construct a new
* #MctSessionLimits. @builder must have been allocated on the stack, and must
* not already be initialised.
*
* Construct the #MctSessionLimits by calling methods on @builder, followed by
* mct_session_limits_builder_end(). To abort construction, use
* mct_session_limits_builder_clear().
*
* Since: 0.5.0
*/
void
mct_session_limits_builder_init (MctSessionLimitsBuilder *builder)
{
MctSessionLimitsBuilder local_builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
g_return_if_fail (_builder != NULL);
g_return_if_fail (_builder->limit_type == MCT_SESSION_LIMITS_TYPE_NONE);
memcpy (builder, &local_builder, sizeof (local_builder));
}
/**
* mct_session_limits_builder_clear:
* @builder: an #MctSessionLimitsBuilder
*
* Clear @builder, freeing any internal state in it. This will not free the
* top-level storage for @builder itself, which is assumed to be allocated on
* the stack.
*
* If called on an already-cleared #MctSessionLimitsBuilder, this function is
* idempotent.
*
* Since: 0.5.0
*/
void
mct_session_limits_builder_clear (MctSessionLimitsBuilder *builder)
{
MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
g_return_if_fail (_builder != NULL);
/* Nothing to free here for now. */
_builder->limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
}
/**
* mct_session_limits_builder_new:
*
* Construct a new #MctSessionLimitsBuilder on the heap. This is intended for
* language bindings. The returned builder must eventually be freed with
* mct_session_limits_builder_free(), but can be cleared zero or more times with
* mct_session_limits_builder_clear() first.
*
* Returns: (transfer full): a new heap-allocated #MctSessionLimitsBuilder
* Since: 0.5.0
*/
MctSessionLimitsBuilder *
mct_session_limits_builder_new (void)
{
g_autoptr(MctSessionLimitsBuilder) builder = NULL;
builder = g_new0 (MctSessionLimitsBuilder, 1);
mct_session_limits_builder_init (builder);
return g_steal_pointer (&builder);
}
/**
* mct_session_limits_builder_copy:
* @builder: an #MctSessionLimitsBuilder
*
* Copy the given @builder to a newly-allocated #MctSessionLimitsBuilder on the
* heap. This is safe to use with cleared, stack-allocated
* #MctSessionLimitsBuilders.
*
* Returns: (transfer full): a copy of @builder
* Since: 0.5.0
*/
MctSessionLimitsBuilder *
mct_session_limits_builder_copy (MctSessionLimitsBuilder *builder)
{
MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
g_autoptr(MctSessionLimitsBuilder) copy = NULL;
MctSessionLimitsBuilderReal *_copy;
g_return_val_if_fail (builder != NULL, NULL);
copy = mct_session_limits_builder_new ();
_copy = (MctSessionLimitsBuilderReal *) copy;
mct_session_limits_builder_clear (copy);
_copy->limit_type = _builder->limit_type;
switch (_builder->limit_type)
{
case MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE:
_copy->daily_schedule.start_time = _builder->daily_schedule.start_time;
_copy->daily_schedule.end_time = _builder->daily_schedule.end_time;
break;
case MCT_SESSION_LIMITS_TYPE_NONE:
default:
break;
}
return g_steal_pointer (&copy);
}
/**
* mct_session_limits_builder_free:
* @builder: a heap-allocated #MctSessionLimitsBuilder
*
* Free an #MctSessionLimitsBuilder originally allocated using
* mct_session_limits_builder_new(). This must not be called on stack-allocated
* builders initialised using mct_session_limits_builder_init().
*
* Since: 0.5.0
*/
void
mct_session_limits_builder_free (MctSessionLimitsBuilder *builder)
{
g_return_if_fail (builder != NULL);
mct_session_limits_builder_clear (builder);
g_free (builder);
}
/**
* mct_session_limits_builder_end:
* @builder: an initialised #MctSessionLimitsBuilder
*
* Finish constructing an #MctSessionLimits with the given @builder, and return
* it. The #MctSessionLimitsBuilder will be cleared as if
* mct_session_limits_builder_clear() had been called.
*
* Returns: (transfer full): a newly constructed #MctSessionLimits
* Since: 0.5.0
*/
MctSessionLimits *
mct_session_limits_builder_end (MctSessionLimitsBuilder *builder)
{
MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
g_autoptr(MctSessionLimits) session_limits = NULL;
g_return_val_if_fail (_builder != NULL, NULL);
/* Build the #MctSessionLimits. */
session_limits = g_new0 (MctSessionLimits, 1);
session_limits->ref_count = 1;
session_limits->user_id = -1;
session_limits->limit_type = _builder->limit_type;
switch (_builder->limit_type)
{
case MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE:
session_limits->daily_start_time = _builder->daily_schedule.start_time;
session_limits->daily_end_time = _builder->daily_schedule.end_time;
break;
case MCT_SESSION_LIMITS_TYPE_NONE:
default:
/* Defaults: */
session_limits->daily_start_time = 0;
session_limits->daily_end_time = 24 * 60 * 60;
break;
}
mct_session_limits_builder_clear (builder);
return g_steal_pointer (&session_limits);
}
/**
* mct_session_limits_builder_set_none:
* @builder: an initialised #MctSessionLimitsBuilder
*
* Unset any session limits currently set in the @builder.
*
* Since: 0.5.0
*/
void
mct_session_limits_builder_set_none (MctSessionLimitsBuilder *builder)
{
MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
g_return_if_fail (_builder != NULL);
/* This will need to free other limit types data first in future. */
_builder->limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
}
/**
* mct_session_limits_builder_set_daily_schedule:
* @builder: an initialised #MctSessionLimitsBuilder
* @start_time_secs: number of seconds since midnight when the users session
* can first start
* @end_time_secs: number of seconds since midnight when the users session can
* last end
*
* Set the session limits in @builder to be a daily schedule, where sessions are
* allowed between @start_time_secs and @end_time_secs every day.
* @start_time_secs and @end_time_secs are given as offsets from the start of
* the day, in seconds. @end_time_secs must be greater than @start_time_secs.
* @end_time_secs must be at most `24 * 60 * 60`.
*
* This will overwrite any other session limits.
*
* Since: 0.5.0
*/
void
mct_session_limits_builder_set_daily_schedule (MctSessionLimitsBuilder *builder,
guint start_time_secs,
guint end_time_secs)
{
MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
g_return_if_fail (_builder != NULL);
g_return_if_fail (start_time_secs < end_time_secs);
g_return_if_fail (end_time_secs <= 24 * 60 * 60);
/* This will need to free other limit types data first in future. */
_builder->limit_type = MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE;
_builder->daily_schedule.start_time = start_time_secs;
_builder->daily_schedule.end_time = end_time_secs;
}

View File

@ -56,4 +56,67 @@ gboolean mct_session_limits_check_time_remaining (MctSessionLimits *limits,
guint64 *time_remaining_secs_out, guint64 *time_remaining_secs_out,
gboolean *time_limit_enabled_out); gboolean *time_limit_enabled_out);
/**
* MctSessionLimitsBuilder:
*
* #MctSessionLimitsBuilder is a stack-allocated mutable structure used to build
* an #MctSessionLimits instance. Use mct_session_limits_builder_init(), various
* method calls to set properties of the session limits, and then
* mct_session_limits_builder_end(), to construct an #MctSessionLimits.
*
* Since: 0.5.0
*/
typedef struct
{
/*< private >*/
guint u0;
guint u1;
guint u2;
gpointer p0[10];
} MctSessionLimitsBuilder;
GType mct_session_limits_builder_get_type (void);
/**
* MCT_SESSION_LIMITS_BUILDER_INIT:
*
* Initialise a stack-allocated #MctSessionLimitsBuilder instance at declaration
* time.
*
* This is typically used with g_auto():
* |[
* g_auto(MctSessionLimitsBuilder) builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
* ]|
*
* Since: 0.5.0
*/
#define MCT_SESSION_LIMITS_BUILDER_INIT() \
{ \
0, /* MCT_SESSION_LIMITS_TYPE_NONE */ \
0, \
0, \
/* padding: */ \
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } \
}
void mct_session_limits_builder_init (MctSessionLimitsBuilder *builder);
void mct_session_limits_builder_clear (MctSessionLimitsBuilder *builder);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (MctSessionLimitsBuilder,
mct_session_limits_builder_clear)
MctSessionLimitsBuilder *mct_session_limits_builder_new (void);
MctSessionLimitsBuilder *mct_session_limits_builder_copy (MctSessionLimitsBuilder *builder);
void mct_session_limits_builder_free (MctSessionLimitsBuilder *builder);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MctSessionLimitsBuilder, mct_session_limits_builder_free)
MctSessionLimits *mct_session_limits_builder_end (MctSessionLimitsBuilder *builder);
void mct_session_limits_builder_set_none (MctSessionLimitsBuilder *builder);
void mct_session_limits_builder_set_daily_schedule (MctSessionLimitsBuilder *builder,
guint start_time_secs,
guint end_time_secs);
G_END_DECLS G_END_DECLS