diff options
-rw-r--r-- | Makefile.common | 3 | ||||
-rw-r--r-- | android/main.c | 6 | ||||
-rw-r--r-- | android/snaphost-android.c | 180 | ||||
-rw-r--r-- | android/snaphost-android.h | 32 | ||||
-rw-r--r-- | android/utils/ini.c | 53 | ||||
-rw-r--r-- | android/utils/ini.h | 17 | ||||
-rw-r--r-- | vl-android.c | 15 |
7 files changed, 294 insertions, 12 deletions
diff --git a/Makefile.common b/Makefile.common index 59c18ea..4a711c6 100644 --- a/Makefile.common +++ b/Makefile.common @@ -429,7 +429,8 @@ CORE_MISC_SOURCES = \ android/camera/camera-format-converters.c \ android/camera/camera-service.c \ android/adb-server.c \ - android/adb-qemud.c + android/adb-qemud.c \ + android/snaphost-android.c $(call gen-hw-config-defs) diff --git a/android/main.c b/android/main.c index 1adb6a1..c56bb5c 100644 --- a/android/main.c +++ b/android/main.c @@ -1308,7 +1308,11 @@ int main(int argc, char **argv) coreHwIniPath = tempfile_path(tempIni); } - if (iniFile_saveToFile(hwIni, coreHwIniPath) < 0) { + /* While saving HW config, ignore valueless entries. This will not break + * anything, but will significantly simplify comparing the current HW + * config with the one that has been associated with a snapshot (in case + * VM starts from a snapshot for this instance of emulator). */ + if (iniFile_saveToFileClean(hwIni, coreHwIniPath) < 0) { derror("Could not write hardware.ini to %s: %s", coreHwIniPath, strerror(errno)); exit(2); } diff --git a/android/snaphost-android.c b/android/snaphost-android.c new file mode 100644 index 0000000..205531c --- /dev/null +++ b/android/snaphost-android.c @@ -0,0 +1,180 @@ +/* Copyright (C) 2012 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +*/ + +#include "qemu-common.h" +#include "android/globals.h" +#include "android/snaphost-android.h" +#include "android/utils/debug.h" + +#define E(...) derror(__VA_ARGS__) +#define W(...) dwarning(__VA_ARGS__) +#define D(...) VERBOSE_PRINT(init,__VA_ARGS__) + +/* Compares two instance of an ini file. + * This routine compares all entries (key,value pairs) found in one ini file + * against entries in another file. The files are considered to be equal if: + * 1. Number of entries in each file is equal. + * 2. Each entry in one file has a corresponded entry in another file, and their + * values are equal. + * Param: + * current - Ini file containing the current configuration. + * saved - Ini file containing a previously saved configuration. + * Return: + * 0 if files are equal, or 1 if they are not equal, or -1 if an error has + * occurred. + */ +static int +_cmp_hw_config(IniFile* current, IniFile* saved) +{ + int n, ret = 0; + const int num_pairs = iniFile_getPairCount(current); + + /* Check 1: must contain same number of entries. */ + if (num_pairs != iniFile_getPairCount(saved)) { + D("Different numbers of entries in the HW config files. Current contans %d, while saved contains %d entries.", + num_pairs, iniFile_getPairCount(saved)); + return -1; + } + + /* Iterate through the entries in the current file, comparing them to entries + * in the saved file. */ + for (n = 0; n < num_pairs && ret == 0; n++) { + char* key, *value1, *value2; + + if (iniFile_getEntry(current, n, &key, &value1)) { + D("Unable to obtain entry %d from the current HW config file", n); + return -1; + } + + value2 = iniFile_getString(saved, key, ""); + if (value2 == NULL) { + D("Saved HW config file is missing entry ('%s', '%s') found in the current HW config.", + key, value1); + free(key); + free(value1); + return 1; + } + + ret = strcmp(value1, value2); + if (ret) { + D("HW config value mismatch for a key '%s': current is '%s' while saved was '%s'", + key, value1, value2); + } + + free(value2); + free(value1); + free(key); + } + + return ret ? 1 : 0; +} + +/* Builds path to the HW config backup file that is used to store HW config + * settings used for that snapshot. The backup path is a concatenation of the + * snapshot storage file path, snapshot name, and an 'ini' extension. This + * way we can track HW configuration for different snapshot names store in + * different storage files. + * Param: + * name - Name of the snapshot inside the snapshot storage file. + * Return: + * Path to the HW config backup file on success, or NULL on an error. + */ +static char* +_build_hwcfg_path(const char* name) +{ + const int path_len = strlen(android_hw->disk_snapStorage_path) + + strlen(name) + 6; + char* bkp_path = malloc(path_len); + if (bkp_path == NULL) { + E("Unable to allocate %d bytes for HW config path!", path_len); + return NULL; + } + + snprintf(bkp_path, path_len, "%s.%s.ini", + android_hw->disk_snapStorage_path, name); + + return bkp_path; +} + +int +snaphost_match_configs(IniFile* hw_ini, const char* name) +{ + /* Make sure that snapshot storage path is valid. */ + if (android_hw->disk_snapStorage_path == NULL || + *android_hw->disk_snapStorage_path == '\0') { + return 1; + } + + /* Build path to the HW config for the loading VM. */ + char* bkp_path = _build_hwcfg_path(name); + if (bkp_path == NULL) { + return 0; + } + + /* Load HW config from the previous emulator launch. */ + IniFile* hwcfg_bkp = iniFile_newFromFile(bkp_path); + + if (hwcfg_bkp != NULL) { + if (_cmp_hw_config(hw_ini, hwcfg_bkp)) { + E("Unable to load VM from snapshot. The snapshot has been saved for a different hardware configuration."); + free(bkp_path); + return 0; + } + iniFile_free(hwcfg_bkp); + } else { + /* It could be that a snapshot file has been copied from another + * location without copying the backup file, or snapshot file has not + * been created yet. In either case we can't do much checking here, + * so, lets be hopefull that user knows what (s)he is doing. */ + D("Missing HW config backup file '%s'", bkp_path); + } + + free(bkp_path); + + return 1; +} + +void +snaphost_save_config(const char* name) +{ + /* Make sure that snapshot storage path is valid. */ + if (android_hw->disk_snapStorage_path == NULL || + *android_hw->disk_snapStorage_path == '\0') { + return; + } + + /* Build path to the HW config for the saving VM. */ + char* bkp_path = _build_hwcfg_path(name); + if (bkp_path == NULL) { + return; + } + + /* Create HW config backup file from the current HW config settings. */ + IniFile* hwcfg_bkp = iniFile_newFromMemory("", bkp_path); + if (hwcfg_bkp == NULL) { + W("Unable to create backup HW config file '%s'. Error: %s", + bkp_path, strerror(errno)); + return; + } + androidHwConfig_write(android_hw, hwcfg_bkp); + + /* Save backup file. */ + if (!iniFile_saveToFileClean(hwcfg_bkp, bkp_path)) { + D("HW config has been backed up to '%s'", bkp_path); + } else { + /* Treat this as a "soft" error. Yes, we couldn't save the backup, but + * this should not cancel snapshot saving. */ + W("Unable to save HW config file '%s'. Error: %s", bkp_path, strerror(errno)); + } + iniFile_free(hwcfg_bkp); + free(bkp_path); +} diff --git a/android/snaphost-android.h b/android/snaphost-android.h new file mode 100644 index 0000000..ae558ba --- /dev/null +++ b/android/snaphost-android.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2012 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +*/ + +#ifndef _ANDROID_SNAPHOST_ANDROID_H_ +#define _ANDROID_SNAPHOST_ANDROID_H_ + +/* Matches HW config saved for a VM snapshot against the current HW config. + * Param: + * hw_ini - IniFile instance containing the current HW config settings. + * name - Name of the snapshot for which the VM is loading. + * Return: + * Boolean: 1 if HW configurations match, or 0 if they don't match. + */ +extern int snaphost_match_configs(IniFile* hw_ini, const char* name); + +/* Saves HW config settings for the current VM. + * Param: + * name - Name of the snapshot for the current VM. + */ +extern void snaphost_save_config(const char* name); + +#endif /* _ANDROID_SNAPHOST_ANDROID_H_ */ + diff --git a/android/utils/ini.c b/android/utils/ini.c index ff4a8af..43f1321 100644 --- a/android/utils/ini.c +++ b/android/utils/ini.c @@ -298,8 +298,18 @@ EXIT: return ini; } -int -iniFile_saveToFile( IniFile* f, const char* filepath ) +/* Common routine for saving IniFile instance to the given file. + * Param: + * f - IniFile instance to save. + * filepath - Path to a file where to save the instance. + * strip - If 1, ignore (don't save) pairs with empty values. If 0, save all + * pairs found in the IniFile instance, including the ones that contain + * empty values. + * Returns: + * 0 on success, -1 on error (see errno for error code) + */ +static int +iniFile_saveToFileCommon( IniFile* f, const char* filepath, int strip ) { FILE* fp = fopen(filepath, "wt"); IniPair* pair = f->pairs; @@ -313,11 +323,13 @@ iniFile_saveToFile( IniFile* f, const char* filepath ) } for ( ; pair < pairEnd; pair++ ) { - char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); - p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value); - if (fwrite(temp, p - temp, 1, fp) != 1) { - result = -1; - break; + if ((pair->value && *pair->value) || !strip) { + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value); + if (fwrite(temp, p - temp, 1, fp) != 1) { + result = -1; + break; + } } } @@ -325,6 +337,33 @@ iniFile_saveToFile( IniFile* f, const char* filepath ) return result; } +int +iniFile_saveToFile( IniFile* f, const char* filepath ) +{ + return iniFile_saveToFileCommon(f, filepath, 0); +} + +int +iniFile_saveToFileClean( IniFile* f, const char* filepath ) +{ + return iniFile_saveToFileCommon(f, filepath, 1); +} + +int +iniFile_getEntry(IniFile* f, int index, char** key, char** value) +{ + if (index >= f->numPairs) { + D("Index %d exceeds the number of ini file entries %d", + index, f->numPairs); + return -1; + } + + *key = ASTRDUP(f->pairs[index].key); + *value = ASTRDUP(f->pairs[index].value); + + return 0; +} + char* iniFile_getString( IniFile* f, const char* key, const char* defaultValue ) { diff --git a/android/utils/ini.h b/android/utils/ini.h index 5730ee0..fd56575 100644 --- a/android/utils/ini.h +++ b/android/utils/ini.h @@ -37,6 +37,11 @@ IniFile* iniFile_newFromFile( const char* filePath); */ int iniFile_saveToFile( IniFile* f, const char* filePath ); +/* try to write an IniFile into a given file, ignorig pairs with empty values. + * returns 0 on success, -1 on error (see errno for error code) + */ +int iniFile_saveToFileClean( IniFile* f, const char* filepath ); + /* free an IniFile object */ void iniFile_free( IniFile* f ); @@ -48,6 +53,18 @@ int iniFile_getPairCount( IniFile* f ); */ const char* iniFile_getValue( IniFile* f, const char* key ); +/* Copies a 'key, value' pair for an entry in the file. + * Param: + * f - Initialized IniFile instance. + * index - Index of the entry to copy. Must be less than value returned from the + * iniFile_getPairCount routine. + * key, value - Receives key, and value strings for the entry. If this routine + * succeeds, the caller must free the buffers allocated for the strings. + * Return: + * 0 on success, -1 if the index exceeds the capacity of the file + */ +int iniFile_getEntry(IniFile* f, int index, char** key, char** value); + /* returns a copy of the value of a given key, or NULL if defaultValue is NULL. * caller must free() it. */ diff --git a/vl-android.c b/vl-android.c index 322ddca..8f439ac 100644 --- a/vl-android.c +++ b/vl-android.c @@ -208,6 +208,8 @@ int qemu_main(int argc, char **argv, char **envp); #include "android/core-init-utils.h" #include "android/audio-test.h" +#include "android/snaphost-android.h" + #ifdef CONFIG_STANDALONE_CORE /* Verbose value used by the standalone emulator core (without UI) */ unsigned long android_verbose; @@ -235,8 +237,6 @@ extern void android_emulator_set_base_port(int port); #include "libslirp.h" #endif - - #define DEFAULT_RAM_SIZE 128 /* Max number of USB devices that can be specified on the commandline. */ @@ -2025,6 +2025,10 @@ static void main_loop(void) no_shutdown = 0; } else { if (savevm_on_exit != NULL) { + /* Prior to saving VM to the snapshot file, save HW config + * settings for that VM, so we can match them when VM gets + * loaded from the snapshot. */ + snaphost_save_config(savevm_on_exit); do_savevm(cur_mon, savevm_on_exit); } break; @@ -3511,6 +3515,12 @@ int main(int argc, char **argv, char **envp) androidHwConfig_init(android_hw, 0); androidHwConfig_read(android_hw, hw_ini); + /* If we're loading VM from a snapshot, make sure that the current HW config + * matches the one with which the VM has been saved. */ + if (loadvm && *loadvm && !snaphost_match_configs(hw_ini, loadvm)) { + exit(0); + } + iniFile_free(hw_ini); { @@ -4229,7 +4239,6 @@ int main(int argc, char **argv, char **envp) stralloc_reset(kernel_config); } - for (env = first_cpu; env != NULL; env = env->next_cpu) { for (i = 0; i < nb_numa_nodes; i++) { if (node_cpumask[i] & (1 << env->cpu_index)) { |