static void
record_network_change (GDBusProxy *dbus_proxy,
                       gchar      *sender_name,
                       gchar      *signal_name,
                       GVariant   *parameters,
                       gpointer    user_data)
{
  if (strcmp ("StateChanged", signal_name) == 0)
    {
      guint32 new_network_state;
      g_variant_get (parameters, "(u)", &new_network_state);

      GVariant *status_change = g_variant_new ("(uu)", previous_network_state,
                                               new_network_state);

      emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                        NETWORK_STATUS_CHANGED_EVENT,
                                        status_change);

      /* schedule recording the network ID provided we have a default route */
      if (new_network_state >= NM_STATE_CONNECTED_SITE)
        {
          g_idle_add ((GSourceFunc) record_network_id, GINT_TO_POINTER (FALSE));
        }

      previous_network_state = new_network_state;
    }
}
static gboolean
record_windows_licenses (gpointer unused)
{
  g_autoptr(GFile) tables = g_file_new_for_path (ACPI_TABLES_PATH);
  guint32 licenses = 0;
  gsize i;

  for (i = 0; i < G_N_ELEMENTS (windows_license_tables); i++)
    {
      const gchar *table_name = windows_license_tables[i];
      g_autoptr(GFile) table = g_file_get_child (tables, table_name);
      gboolean present = g_file_query_exists (table, NULL);

      g_debug ("ACPI table %s is %s",
               table_name,
               present ? "present" : "absent");

      if (present)
        licenses |= 1 << i;
    }

  emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                    WINDOWS_LICENSE_TABLES_EVENT,
                                    g_variant_new_uint32 (licenses));

  return G_SOURCE_REMOVE;
}
/*
 * Handle a signal from the systemd manager by recording the StartupFinished
 * signal. Once the StartupFinished signal has been received, call the
 * Unsubscribe method on the systemd manager interface to stop requesting that
 * all signals be emitted.
 */
static void
record_startup (GDBusProxy *dbus_proxy,
                gchar      *sender_name,
                gchar      *signal_name,
                GVariant   *parameters,
                gpointer    user_data)
{
  if (strcmp (signal_name, "StartupFinished") == 0)
    {
      emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                        STARTUP_FINISHED, parameters);

      GError *error = NULL;
      GVariant *unsubscribe_result =
        g_dbus_proxy_call_sync (dbus_proxy, "Unsubscribe",
                                NULL /* parameters */,
                                G_DBUS_CALL_FLAGS_NONE, -1 /* timeout */,
                                NULL /* GCancellable */, &error);
      if (unsubscribe_result == NULL)
        {
          g_warning ("Error unsubscribing from systemd signals: %s.",
                     error->message);
          g_error_free (error);
          return;
        }

      g_variant_unref (unsubscribe_result);
    }
}
static gboolean
record_live_boot (gpointer unused)
{
  gboolean is_live_boot, is_dual_boot;

  check_cmdline (&is_live_boot, &is_dual_boot);

  if (is_live_boot)
    emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                      LIVE_BOOT_EVENT, NULL);
  else if (is_dual_boot)
    emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                      DUAL_BOOT_EVENT, NULL);

  return G_SOURCE_REMOVE;
}
/* Records a system shutdown event. Reports the running uptime tally that spans
 * across boots and the boot count as the auxiliary payload of the system
 * shutdown event.
 */
static void
record_shutdown (void)
{
  GVariant *uptime_payload = make_uptime_payload ();
  emtr_event_recorder_record_event_sync (emtr_event_recorder_get_default (),
                                         SHUTDOWN_EVENT, uptime_payload);

  g_object_unref (persistent_tally);
}
static void
remove_session (const gchar *session_id)
{
  if (!remove_session_from_set (session_id))
    return;

  emtr_event_recorder_record_stop_sync (emtr_event_recorder_get_default (),
                                        USER_IS_LOGGED_IN,
                                        g_variant_new_string (session_id),
                                        NULL /* auxiliary_payload */);
}
/* Intended for use as a GSourceFunc callback. Records an uptime event. Reports
 * the running uptime tally that spans across boots and the boot count as the
 * auxiliary payload of the system shutdown event.
 */
static gboolean
record_uptime (gpointer unused)
{
  GVariant *uptime_payload = make_uptime_payload ();
  emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                    UPTIME_EVENT, uptime_payload);
  g_timeout_add_seconds (RECORD_UPTIME_INTERVAL_SECONDS,
                         (GSourceFunc) record_uptime,
                         NULL);
  return G_SOURCE_REMOVE;
}
/*
 * Intended for use as a GDataForeachFunc callback. Synchronously records a
 * logout for the given session ID.
 */
static void
record_stop_for_login (GQuark   session_id_quark,
                       gpointer unused,
                       gpointer user_data)
{
  const gchar *session_id = g_quark_to_string (session_id_quark);
  GVariant *session_id_variant = g_variant_new_string (session_id);
  emtr_event_recorder_record_stop_sync (emtr_event_recorder_get_default (),
                                        USER_IS_LOGGED_IN,
                                        session_id_variant,
                                        NULL /* auxiliary_payload */);
}
static gboolean
record_image_version (gpointer unused)
{
  g_autofree gchar *image_version = NULL;

  image_version = get_image_version ();

  if (image_version != NULL)
    emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                      EOS_IMAGE_VERSION_EVENT,
                                      g_variant_new_string (image_version));

  return G_SOURCE_REMOVE;
}
static void
add_session (const gchar *session_id,
             guint32      user_id)
{
  if (!add_session_to_set (session_id))
    return;

  GVariant *user_id_variant =
    (user_id >= MIN_HUMAN_USER_ID) ? g_variant_new_uint32 (user_id) : NULL;

  emtr_event_recorder_record_start (emtr_event_recorder_get_default (),
                                    USER_IS_LOGGED_IN,
                                    g_variant_new_string (session_id),
                                    user_id_variant);
}
static gboolean
record_location_label (gpointer unused)
{
  g_autoptr (GError) err = NULL;
  g_autoptr (GKeyFile) kf = g_key_file_new ();

  if (!g_key_file_load_from_file (kf, LOCATION_CONF_FILE, G_KEY_FILE_NONE, &err))
    {
      /* this file’s existence is optional, so not found is not an error */
      if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
        return G_SOURCE_REMOVE;

      g_warning ("Failed to load " LOCATION_CONF_FILE ", unable to record location label: %s",
                 err->message);
      return G_SOURCE_REMOVE;
    }

  g_auto (GStrv) keys = g_key_file_get_keys (kf, LOCATION_LABEL_GROUP, NULL, NULL);
  if (keys == NULL || *keys == NULL)
    return G_SOURCE_REMOVE;

  g_autoptr (GString) label = g_string_new ("");
  g_auto (GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_ARRAY);
  for (GStrv cur = keys; *cur != NULL; cur++)
    {
      const gchar *key = *cur;
      g_autofree gchar *val = g_key_file_get_string (kf, LOCATION_LABEL_GROUP, key, NULL);

      if (val == NULL)
        continue;

      if (cur != keys)
        g_string_append (label, ", ");

      g_variant_builder_add (&builder, "{ss}", key, val);
      g_string_append_printf (label, "\"%s\" = \"%s\"", key, val);
    }

  g_message ("Recording location label: %s", label->str);

  emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                    LOCATION_LABEL_EVENT,
                                    g_variant_builder_end (&builder));

  return G_SOURCE_REMOVE;
}
static gboolean
record_os_version (gpointer unused)
{
  gchar *os_name = NULL;
  gchar *os_version = NULL;

  if (!get_os_version (&os_name, &os_version))
    return G_SOURCE_REMOVE;

  GVariant *payload = g_variant_new ("(sss)",
                                     os_name, os_version, "");
  emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                    OS_VERSION_EVENT, payload);

  g_free (os_name);
  g_free (os_version);

  return G_SOURCE_REMOVE;
}
static void
maybe_send_metric (EosMetricsInfo *metrics)
{
#ifdef HAS_EOSMETRICS_0
  static gboolean metric_sent = FALSE;

  if (metric_sent)
    return;

  g_message ("Recording metric event %s: (%s, %s, %s)",
             EOS_UPDATER_BRANCH_SELECTED, metrics->vendor, metrics->product,
             metrics->ref);
  emtr_event_recorder_record_event_sync (emtr_event_recorder_get_default (),
                                         EOS_UPDATER_BRANCH_SELECTED,
                                         g_variant_new ("(sssb)", metrics->vendor,
                                                        metrics->product,
                                                        metrics->ref,
                                                        (gboolean) FALSE  /* on-hold */));
  metric_sent = TRUE;
#endif
}
static gboolean
record_network_id (gpointer force_ptr)
{
  gboolean force = GPOINTER_TO_INT (force_ptr);
  guint32 network_id;

  if (!eins_network_id_get (&network_id))
    {
      return G_SOURCE_REMOVE;
    }

  if (network_id != previous_network_id || force)
    {
      g_message ("Recording network ID: %8x", network_id);

      emtr_event_recorder_record_event (emtr_event_recorder_get_default (),
                                        NETWORK_ID_EVENT,
                                        g_variant_new_uint32 (network_id));

      previous_network_id = network_id;
    }

  return G_SOURCE_REMOVE;
}