/* * OProfile User Interface * * Copyright (C) 2007 Nokia Corporation. All rights reserved. * * Author: Robert Bradford * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "oprofileui.h" #include "response.h" #include "command.h" #include "client.h" #include "archive_window.h" #include "archive_save_window.h" #include "main.h" #include "archive.h" /* The current file downloading */ static gchar *archive_path; static gchar *archive_file; static uint64_t archive_mtime; /* The files that make up the active profile */ static GSList *cached_files; static GSList *stashed_files; static GSList *downloaded_files; static gchar *archive_basepath; static GSList *archive_files; static gboolean user_specified = FALSE; /* Whether the current path is user specified */ /* Copy a file from src to dest, optionally hardlinking when possible */ static void archive_copyfile (gchar *src, gchar *dest, gboolean hardlink) { int ret = -1; g_mkdir_with_parents (g_path_get_dirname (dest), 00700); g_remove (dest); /* Initially try to hardlink the file */ if (hardlink) ret = link (src, dest); if (ret < 0) { /* Use gio to copy the file as a fallback */ GFile *src_file, *dst_file; gboolean res; GError *error = NULL; src_file = g_file_new_for_path (src); dst_file = g_file_new_for_path (dest); res = g_file_copy(src_file, dst_file, G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error); if (!res && error) { printf ("GIO: error %s (%s to %s)\n", error->message, src, dest); g_error_free(error); } g_object_unref(src_file); g_object_unref(dst_file); } } #define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## KIND)) static gboolean remove_target_recursively(GFile *file) { GFileEnumerator *enumerator; GError *error = NULL; GFile *child; GFileInfo *info; gboolean stop = FALSE; enumerator = g_file_enumerate_children(file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); if (enumerator) { error = NULL; while ( (info = g_file_enumerator_next_file(enumerator, NULL, &error)) != NULL ) { child = g_file_get_child(file, g_file_info_get_name(info)); if (!remove_target_recursively(child)) { stop = TRUE; break; } g_object_unref(child); g_object_unref(info); } g_object_unref(enumerator); } else if (IS_IO_ERROR(error, NOT_DIRECTORY)) { g_error_free(error); } else { g_error_free(error); stop = TRUE; } if (stop) return FALSE; error = NULL; if (!g_file_delete(file, NULL, &error)) { char *path = g_file_get_path(file); printf ("GIO: error %s when deleteing file %s.\n", error->message, path); g_free(path); g_error_free(error); return FALSE; } return TRUE; } /* Delete the directory specified by path */ static void archive_removedir(gchar *path) { GFile *src_file; if (path == NULL) return; src_file = g_file_new_for_path (path); if (! remove_target_recursively(src_file)) { printf ("GIO:remove %s failed", path); } } static gchar * archive_get_archive_basepath () { static const gchar *tmpdir; if (archive_basepath) return archive_basepath; if (tmpdir == NULL) { tmpdir = g_getenv("TMPDIR"); if (tmpdir == NULL) { tmpdir = "/tmp"; } } archive_basepath = g_strdup_printf ("%s/oprofile-archive-%d", tmpdir, getpid()); return archive_basepath; } static void archive_clear_archive_basepath (void) { g_free (archive_basepath); archive_basepath = NULL; } gchar * archive_get_archive_path () { if (archive_path) return archive_path; archive_path = g_strdup_printf ("%s/active", archive_get_archive_basepath()); user_specified = FALSE; return archive_path; } void archive_set_archive_path (gchar *path) { g_free (archive_path); if (path == NULL) archive_path = NULL; else archive_path = g_strdup (path); user_specified = TRUE; } static void archive_fully_downloaded () { archive_window_finished (); g_slist_free (archive_files); archive_files=NULL; main_archive_finished_cb (TRUE, NULL); } static void archive_get_next_file (Client *client) { static GSList *cur_entry = NULL; if (cur_entry == NULL) { cur_entry = archive_files; } else if (g_slist_next (cur_entry) == NULL) { /* Important reset the cur_entry pointer */ cur_entry = NULL; archive_fully_downloaded (); return; } else { cur_entry = g_slist_next (cur_entry); } /* Fire off a request to download the next file */ archive_file = cur_entry->data; archive_window_file_get_started (archive_file); client_send_command_filestat (client, archive_file); /* Yield to gtk. */ while (gtk_events_pending ()) gtk_main_iteration (); } void archive_handle_response (Client *client, OpuiResponse *reply) { guint archive_count; gchar **tmp; int i; tmp = g_strsplit (reply->payload, "\n", 0); for (i=0; i < g_strv_length (tmp); i++) { gchar *str = g_strdup(tmp[i]); g_strstrip (str); if (!g_str_equal (str, "")) archive_files = g_slist_append (archive_files, str); } archive_count = g_slist_length (archive_files); archive_window_file_list_finished (archive_count); /* Bunch of initialisation stuff */ /* Delete the old archive */ archive_cleanup (); archive_set_archive_path (NULL); archive_get_next_file (client); g_strfreev (tmp); } static gchar * archive_cache_filename (gchar *filename) { if (opui_config->localhost) return g_strdup_printf("%s/localhost%s", archive_get_archive_basepath(), filename); return g_strdup_printf("%s/%s-%d%s", archive_get_archive_basepath(), opui_config->host, opui_config->port, filename); } void archive_file_got (Client *client) { struct utimbuf utime_buf; struct stat stat_details; gchar *src, *dest; int ret; archive_window_file_get_finished (); src = g_strdup_printf("%s%s", archive_get_archive_path(), archive_file); ret = stat (src, &stat_details); /* Only proceed if the file exists */ if (ret >= 0) { downloaded_files = g_slist_append (downloaded_files, archive_file); /* Set the mtime to match the original file */ utime_buf.actime = archive_mtime; utime_buf.modtime = archive_mtime; utime (src, &utime_buf); /* Try to hardlink a cache copy of the downloaded file */ dest = archive_cache_filename (archive_file); archive_copyfile(src, dest, TRUE); utime (dest, &utime_buf); g_free(dest); } g_free(src); archive_get_next_file (client); } void archive_filestat_got (Client *client, uint64_t mtime, uint64_t size, uint64_t mode) { struct stat stat_details; gboolean needfile = TRUE, needcopy = TRUE; gchar *cache, *dest; int ret; cache = archive_cache_filename (archive_file); archive_mtime = mtime; /* Do we need to download the file? */ /* Ignore directories */ if (S_ISDIR(mode)) { needfile = FALSE; needcopy = FALSE; } /* Can we use the binary stash? */ else if (strncmp(archive_file, "/var/lib/oprofile", strlen("/var/lib/oprofile"))) { if (opui_config->localhost) { needfile = FALSE; needcopy = FALSE; stashed_files = g_slist_append (stashed_files, archive_file); } else { gchar *cache2 = g_strdup_printf("%s%s", opui_config->binaries_location, archive_file); ret = stat (cache2, &stat_details); if ((ret >= 0) && (stat_details.st_mtime == mtime) && (stat_details.st_size == size)) { /*printf("Match: %s %d - %lld, %d - %lld\n", cache2, stat_details.st_mtime, mtime, stat_details.st_size, size);*/ needfile = FALSE; needcopy = FALSE; stashed_files = g_slist_append (stashed_files, archive_file); } else { /*printf("No Match: %s %d - %lld, %d - %lld\n", cache2, stat_details.st_mtime, mtime, stat_details.st_size, size);*/ } g_free(cache2); } } /* Can we use our cache? */ if (needfile) { ret = stat (cache, &stat_details); if ((ret >= 0) && (stat_details.st_mtime == mtime) && (stat_details.st_size == size)) { needfile = FALSE; cached_files = g_slist_append (cached_files, archive_file); } } /* Act accordingly */ if (needfile) { client_send_command_get (client, archive_file); } else { if (needcopy) { dest = g_strdup_printf("%s%s", archive_get_archive_path(), archive_file); archive_copyfile(cache, dest, TRUE); g_free(dest); } archive_window_file_get_finished (); archive_get_next_file (client); } g_free(cache); } static void archive_save_copy(GSList *entry, gchar * (*from_func) (gchar *filename), gchar *to, gboolean hardlink, gint *counter) { while (entry) { gchar *file = entry->data; gchar *src = from_func(file); gchar *dst = g_strdup_printf("%s%s", to, file); archive_copyfile(src, dst, hardlink); g_free(dst); g_free(src); entry = g_slist_next (entry); archive_save_window_progress (*counter); /* Yield to gtk. */ while (gtk_events_pending ()) gtk_main_iteration (); *counter = *counter + 1; } } static gchar * archive_downloaded_filename (gchar *filename) { return g_strdup_printf("%s%s", archive_path, filename); } static gchar * archive_stashed_filename (gchar *filename) { return g_strdup_printf("%s%s", opui_config->binaries_location, filename); } void archive_save (gchar *path) { gint counter; downloaded_files = g_slist_append (downloaded_files, g_strdup("/var/lib/oprofile/.converted")); archive_save_window_show (g_slist_length (downloaded_files) + g_slist_length (stashed_files) + g_slist_length (cached_files)); archive_save_copy (downloaded_files, archive_downloaded_filename, path, TRUE, &counter); archive_save_copy (stashed_files, archive_stashed_filename, path, TRUE, &counter); archive_save_copy (cached_files, archive_cache_filename, path, TRUE, &counter); archive_save_window_finished (); archive_cleanup (); /* Set the new archive path */ archive_set_archive_path (path); } void archive_load_readdir(gchar *path) { GDir *dir; const gchar *entry; gchar *entry_path; dir = g_dir_open(path, 0, NULL); entry = g_dir_read_name (dir); while (entry) { entry_path = g_strdup_printf("%s/%s", path, entry); if (g_file_test (entry_path, G_FILE_TEST_IS_DIR) ) archive_load_readdir(entry_path); else downloaded_files = g_slist_append (downloaded_files, entry_path); g_free (entry_path); entry = g_dir_read_name (dir); } g_dir_close (dir); } void archive_load (gchar *path) { /* Cleanup the old archive */ archive_cleanup (); archive_set_archive_path (path); archive_load_readdir (path); } void archive_cleanup () { g_slist_free (cached_files); cached_files = NULL; g_slist_free (stashed_files); stashed_files = NULL; g_slist_free (downloaded_files); downloaded_files = NULL; if (archive_path == NULL) return; if (user_specified) return; archive_removedir (archive_path); archive_set_archive_path (NULL); } void archive_full_cleanup () { archive_cleanup (); if (archive_basepath == NULL) return; archive_removedir (archive_basepath); archive_clear_archive_basepath (); }