Back to index

indicator-appmenu  12.10.0
Classes | Defines | Enumerations | Functions
usage-tracker.c File Reference
#include "usage-tracker.h"
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <sqlite3.h>
#include "load-app-info.h"
#include "create-db.h"
#include "hudsettings.h"

Go to the source code of this file.

Classes

struct  _UsageTrackerPrivate

Defines

#define G_LOG_DOMAIN   "usagetracker"
#define SQL_VARS_APPLICATION   "1"
#define SQL_VARS_ENTRY   "2"
#define USAGE_TRACKER_GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), USAGE_TRACKER_TYPE, UsageTrackerPrivate))

Enumerations

enum  sql_variables { SQL_VAR_APPLICATION = 1, SQL_VAR_ENTRY = 2 }

Functions

static void usage_tracker_dispose (GObject *object)
static void usage_tracker_finalize (GObject *object)
static void cleanup_db (UsageTracker *self)
static void configure_db (UsageTracker *self)
static void prepare_statements (UsageTracker *self)
static void build_db (UsageTracker *self)
static gboolean drop_entries (gpointer user_data)
static void check_app_init (UsageTracker *self, const gchar *application)
 G_DEFINE_TYPE (UsageTracker, usage_tracker, G_TYPE_OBJECT)
static void usage_tracker_class_init (UsageTrackerClass *klass)
static void usage_tracker_init (UsageTracker *self)
UsageTracker * usage_tracker_new (void)
void usage_tracker_mark_usage (UsageTracker *self, const gchar *application, const gchar *entry)
guint usage_tracker_get_usage (UsageTracker *self, const gchar *application, const gchar *entry)
UsageTracker * usage_tracker_get_instance (void)

Class Documentation

struct _UsageTrackerPrivate

Definition at line 39 of file usage-tracker.c.

Class Members
sqlite3_stmt * application_count
gchar * cachefile
sqlite3 * db
sqlite3_stmt * delete_aged
guint drop_timer
sqlite3_stmt * entry_count
sqlite3_stmt * insert_entry

Define Documentation

#define G_LOG_DOMAIN   "usagetracker"

Definition at line 23 of file usage-tracker.c.

#define SQL_VARS_APPLICATION   "1"

Definition at line 56 of file usage-tracker.c.

#define SQL_VARS_ENTRY   "2"

Definition at line 57 of file usage-tracker.c.

#define USAGE_TRACKER_GET_PRIVATE (   o)    (G_TYPE_INSTANCE_GET_PRIVATE ((o), USAGE_TRACKER_TYPE, UsageTrackerPrivate))

Definition at line 59 of file usage-tracker.c.


Enumeration Type Documentation

Enumerator:
SQL_VAR_APPLICATION 
SQL_VAR_ENTRY 

Definition at line 51 of file usage-tracker.c.


Function Documentation

static void build_db ( UsageTracker *  self) [static]

Definition at line 322 of file usage-tracker.c.

{
       g_debug("New database, initializing");

       /* Create the table */
       int exec_status = SQLITE_OK;
       gchar * failstring = NULL;
       exec_status = sqlite3_exec(self->priv->db,
                                  create_db,
                                  NULL, NULL, &failstring);
       if (exec_status != SQLITE_OK) {
              g_warning("Unable to create table: %s", failstring);
       }

       return;
}

Here is the caller graph for this function:

static void check_app_init ( UsageTracker *  self,
const gchar *  application 
) [static]

Definition at line 442 of file usage-tracker.c.

{
       sqlite3_reset(self->priv->application_count);

       int bind_status = SQLITE_OK;
       bind_status = sqlite3_bind_text(self->priv->application_count, SQL_VAR_APPLICATION, application, -1, SQLITE_TRANSIENT);
       if (bind_status != SQLITE_OK) {
              g_warning("Unable to bind application info: %s", sqlite3_errmsg(self->priv->db));
              return;
       }

       int exec_status = SQLITE_ROW;
       guint count = 0;

       while ((exec_status = sqlite3_step(self->priv->application_count)) == SQLITE_ROW) {
              count = sqlite3_column_int(self->priv->application_count, 0);
       }

       if (exec_status != SQLITE_DONE) {
              g_warning("Unknown status from executing application_count: %d", exec_status);
       }

       if (count > 0) {
              return;
       }

       g_debug("Initializing application: %s", application);
       gchar * basename = g_path_get_basename(application);

       gchar * app_info_path = NULL;

       if (g_getenv("HUD_APP_INFO_DIR") != NULL) {
              app_info_path = g_strdup(g_getenv("HUD_APP_INFO_DIR"));
       } else {
              app_info_path = g_build_filename(DATADIR, "indicator-appmenu", "hud", "app-info", NULL);
       }

       gchar * app_info_filename = g_strdup_printf("%s.hud-app-info", basename);
       gchar * app_info = g_build_filename(app_info_path, app_info_filename, NULL);

       if (!load_app_info(app_info, self->priv->db)) {
              if (g_file_test(app_info, G_FILE_TEST_EXISTS)) {
                     g_warning("Unable to load application information for application '%s' at path '%s'", application, app_info);
              }
       }

       g_free(app_info);
       g_free(app_info_filename);
       g_free(app_info_path);
       g_free(basename);

       return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void cleanup_db ( UsageTracker *  self) [static]

Definition at line 141 of file usage-tracker.c.

{
       if (self->priv->insert_entry != NULL) {
              sqlite3_finalize(self->priv->insert_entry);
              self->priv->insert_entry = NULL;
       }

       if (self->priv->entry_count != NULL) {
              sqlite3_finalize(self->priv->entry_count);
              self->priv->entry_count = NULL;
       }

       if (self->priv->delete_aged != NULL) {
              sqlite3_finalize(self->priv->delete_aged);
              self->priv->delete_aged = NULL;
       }

       if (self->priv->application_count != NULL) {
              sqlite3_finalize(self->priv->application_count);
              self->priv->application_count = NULL;
       }

       if (self->priv->db != NULL) {
              sqlite3_close(self->priv->db);
              self->priv->db = NULL;
       }

       return;
}

Here is the caller graph for this function:

static void configure_db ( UsageTracker *  self) [static]

Definition at line 179 of file usage-tracker.c.

{
       /* Removing the previous database */
       cleanup_db(self);

       if (self->priv->cachefile != NULL) {
              g_free(self->priv->cachefile);
              self->priv->cachefile = NULL;
       }
       
       /* Determine where his database should be built */
       gboolean usage_data = hud_settings.store_usage_data;

       if (g_getenv("HUD_NO_STORE_USAGE_DATA") != NULL) {
              usage_data = FALSE;
       }

       if (usage_data) {
              g_debug("Storing usage data on filesystem");
       }

       /* Setting up the new database */
       gboolean db_exists = FALSE;

       if (usage_data) {
              /* If we're storing the usage data we need to figure out
                 how to do it on disk */

              const gchar * basecachedir = g_getenv("HUD_CACHE_DIR");
              if (basecachedir == NULL) {
                     basecachedir = g_get_user_cache_dir();
              }

              gchar * cachedir = g_build_filename(basecachedir, "indicator-appmenu", NULL);
              if (!g_file_test(cachedir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
                     g_mkdir(cachedir, 1 << 6 | 1 << 7 | 1 << 8); // 700
              }
              g_free(cachedir);

              self->priv->cachefile = g_build_filename(basecachedir, "indicator-appmenu", "hud-usage-log.sqlite", NULL);
              db_exists = g_file_test(self->priv->cachefile, G_FILE_TEST_EXISTS);
              int open_status = sqlite3_open(self->priv->cachefile, &self->priv->db); 

              if (open_status != SQLITE_OK) {
                     g_warning("Error building LRU DB");
                     sqlite3_close(self->priv->db);
                     self->priv->db = NULL;
              }
       } else {
              /* If we're not storing it, let's make an in memory database
                 so that we can use the app-info, and get better, but we don't
                 give anyone that data. */
              self->priv->cachefile = g_strdup(":memory:");

              int open_status = sqlite3_open(self->priv->cachefile, &self->priv->db); 

              if (open_status != SQLITE_OK) {
                     g_warning("Error building LRU DB");
                     sqlite3_close(self->priv->db);
                     self->priv->db = NULL;
              }
       }

       if (self->priv->db != NULL && !db_exists) {
              build_db(self);
       }

       prepare_statements(self);

       drop_entries(self);

       return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static gboolean drop_entries ( gpointer  user_data) [static]

Definition at line 419 of file usage-tracker.c.

{
       g_return_val_if_fail(IS_USAGE_TRACKER(user_data), FALSE);
       UsageTracker * self = USAGE_TRACKER(user_data);

       if (self->priv->db == NULL) {
              return TRUE;
       }

       sqlite3_reset(self->priv->delete_aged);

       int exec_status = SQLITE_ROW;
       while ((exec_status = sqlite3_step(self->priv->delete_aged)) == SQLITE_ROW) {
       }

       if (exec_status != SQLITE_DONE) {
              g_warning("Unknown status from executing delete_aged: %d", exec_status);
       }

       return TRUE;
}

Here is the caller graph for this function:

G_DEFINE_TYPE ( UsageTracker  ,
usage_tracker  ,
G_TYPE_OBJECT   
)
static void prepare_statements ( UsageTracker *  self) [static]

Definition at line 255 of file usage-tracker.c.

{
       if (self->priv->db == NULL) {
              return;
       }

       /* These should never happen, but let's just check to make sure */
       g_return_if_fail(self->priv->insert_entry == NULL);
       g_return_if_fail(self->priv->entry_count == NULL);
       g_return_if_fail(self->priv->delete_aged == NULL);
       g_return_if_fail(self->priv->application_count == NULL);

       int prepare_status = SQLITE_OK;

       /* Insert Statement */
       prepare_status = sqlite3_prepare_v2(self->priv->db,
                                           "insert into usage (application, entry, timestamp) values (?" SQL_VARS_APPLICATION ", ?" SQL_VARS_ENTRY ", date('now', 'utc'));",
                                           -1, /* length */
                                           &(self->priv->insert_entry),
                                           NULL); /* unused stmt */

       if (prepare_status != SQLITE_OK) {
              g_warning("Unable to prepare insert entry statement: %s", sqlite3_errmsg(self->priv->db));
              self->priv->insert_entry = NULL;
       }

       /* Entry Count Statement */
       prepare_status = sqlite3_prepare_v2(self->priv->db,
                                           "select count(*) from usage where application = ?" SQL_VARS_APPLICATION " and entry = ?" SQL_VARS_ENTRY " and timestamp > date('now', 'utc', '-30 days');",
                                           -1, /* length */
                                           &(self->priv->entry_count),
                                           NULL); /* unused stmt */

       if (prepare_status != SQLITE_OK) {
              g_warning("Unable to prepare entry count statement: %s", sqlite3_errmsg(self->priv->db));
              self->priv->entry_count = NULL;
       }

       /* Delete Aged Statement */
       prepare_status = sqlite3_prepare_v2(self->priv->db,
                                           "delete from usage where timestamp < date('now', 'utc', '-30 days');",
                                           -1, /* length */
                                           &(self->priv->delete_aged),
                                           NULL); /* unused stmt */

       if (prepare_status != SQLITE_OK) {
              g_warning("Unable to prepare delete aged statement: %s", sqlite3_errmsg(self->priv->db));
              self->priv->delete_aged = NULL;
       }

       /* Application Count Statement */
       prepare_status = sqlite3_prepare_v2(self->priv->db,
                                           "select count(*) from usage where application = ?" SQL_VARS_APPLICATION ";",
                                           -1, /* length */
                                           &(self->priv->application_count),
                                           NULL); /* unused stmt */

       if (prepare_status != SQLITE_OK) {
              g_warning("Unable to prepare application count statement: %s", sqlite3_errmsg(self->priv->db));
              self->priv->application_count = NULL;
       }

       return;
}

Here is the caller graph for this function:

static void usage_tracker_class_init ( UsageTrackerClass *  klass) [static]

Definition at line 74 of file usage-tracker.c.

{
       GObjectClass *object_class = G_OBJECT_CLASS (klass);

       g_type_class_add_private (klass, sizeof (UsageTrackerPrivate));

       object_class->dispose = usage_tracker_dispose;
       object_class->finalize = usage_tracker_finalize;

       return;
}

Here is the call graph for this function:

static void usage_tracker_dispose ( GObject *  object) [static]

Definition at line 109 of file usage-tracker.c.

{
       UsageTracker * self = USAGE_TRACKER(object);

       cleanup_db(self);

       if (self->priv->drop_timer != 0) {
              g_source_remove(self->priv->drop_timer);
              self->priv->drop_timer = 0;
       }

       G_OBJECT_CLASS (usage_tracker_parent_class)->dispose (object);
       return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void usage_tracker_finalize ( GObject *  object) [static]

Definition at line 125 of file usage-tracker.c.

{
       UsageTracker * self = USAGE_TRACKER(object);

       if (self->priv->cachefile != NULL) {
              g_free(self->priv->cachefile);
              self->priv->cachefile = NULL;
       }

       G_OBJECT_CLASS (usage_tracker_parent_class)->finalize (object);
       return;
}

Here is the caller graph for this function:

UsageTracker* usage_tracker_get_instance ( void  )

Definition at line 497 of file usage-tracker.c.

{
  static UsageTracker *usage_tracker_instance;

  if (usage_tracker_instance == NULL)
    usage_tracker_instance = usage_tracker_new ();

  return usage_tracker_instance;
}

Here is the call graph for this function:

Here is the caller graph for this function:

guint usage_tracker_get_usage ( UsageTracker *  self,
const gchar *  application,
const gchar *  entry 
)

Definition at line 377 of file usage-tracker.c.

{
       g_return_val_if_fail(IS_USAGE_TRACKER(self), 0);
       g_return_val_if_fail(self->priv->db != NULL, 0);

       check_app_init(self, application);

       sqlite3_reset(self->priv->entry_count);

       int bind_status = SQLITE_OK;

       bind_status = sqlite3_bind_text(self->priv->entry_count, SQL_VAR_APPLICATION, application, -1, SQLITE_TRANSIENT);
       if (bind_status != SQLITE_OK) {
              g_warning("Unable to bind application info: %s", sqlite3_errmsg(self->priv->db));
              return 0;
       }

       bind_status = sqlite3_bind_text(self->priv->entry_count, SQL_VAR_ENTRY, entry, -1, SQLITE_TRANSIENT);
       if (bind_status != SQLITE_OK) {
              g_warning("Unable to bind entry info: %s", sqlite3_errmsg(self->priv->db));
              return 0;
       }

       int exec_status = SQLITE_ROW;
       guint count = 0;

       while ((exec_status = sqlite3_step(self->priv->entry_count)) == SQLITE_ROW) {
              count = sqlite3_column_int(self->priv->entry_count, 0);
       }

       if (exec_status != SQLITE_DONE) {
              g_warning("Unknown status from executing entry_count: %d", exec_status);
       }

       g_debug ("Usage of %s %s is %u", application, entry, count);

       return count;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void usage_tracker_init ( UsageTracker *  self) [static]

Definition at line 87 of file usage-tracker.c.

{
       self->priv = USAGE_TRACKER_GET_PRIVATE(self);

       self->priv->cachefile = NULL;
       self->priv->db = NULL;
       self->priv->drop_timer = 0;

       self->priv->insert_entry = NULL;
       self->priv->entry_count = NULL;
       self->priv->delete_aged = NULL;
       self->priv->application_count = NULL;

       configure_db(self);

       /* Drop entries daily if we run for a really long time */
       self->priv->drop_timer = g_timeout_add_seconds(24 * 60 * 60, drop_entries, self);
       
       return;
}

Here is the call graph for this function:

void usage_tracker_mark_usage ( UsageTracker *  self,
const gchar *  application,
const gchar *  entry 
)

Definition at line 340 of file usage-tracker.c.

{
       g_return_if_fail(IS_USAGE_TRACKER(self));
       g_return_if_fail(self->priv->db != NULL);

       g_debug ("Marking %s %s", application, entry);

       check_app_init(self, application);

       sqlite3_reset(self->priv->insert_entry);

       int bind_status = SQLITE_OK;

       bind_status = sqlite3_bind_text(self->priv->insert_entry, SQL_VAR_APPLICATION, application, -1, SQLITE_TRANSIENT);
       if (bind_status != SQLITE_OK) {
              g_warning("Unable to bind application info: %s", sqlite3_errmsg(self->priv->db));
              return;
       }

       bind_status = sqlite3_bind_text(self->priv->insert_entry, SQL_VAR_ENTRY, entry, -1, SQLITE_TRANSIENT);
       if (bind_status != SQLITE_OK) {
              g_warning("Unable to bind entry info: %s", sqlite3_errmsg(self->priv->db));
              return;
       }

       int exec_status = SQLITE_ROW;
       while ((exec_status = sqlite3_step(self->priv->insert_entry)) == SQLITE_ROW) {
       }

       if (exec_status != SQLITE_DONE) {
              g_warning("Unknown status from executing insert_entry: %d", exec_status);
       }

       return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

UsageTracker* usage_tracker_new ( void  )

Definition at line 172 of file usage-tracker.c.

{
       return g_object_new(USAGE_TRACKER_TYPE, NULL);
}

Here is the caller graph for this function: