More explanations about this patch and the other patch about "stats: add "StatsDir" configuration to define the directory of stats file."

The STORAGEDIR (defined as /var/lib/connman) includes xxx.config, settings and wifi_xxxx directories.  Normally it is put to non-volatile memory(i.e NAND flash).  The "settings" and "data" file under wifi_xxx directory are updated frequently.  Whenever the connection goes to READY state, both files are updated.  And there are other situation of updating the "data" files too. This will cause the flash wear-out quickly. Normally the flash has 100,000 program/erase cycle. If a unit kept disconnecting and reconnecting, it is very bad for the flash.

So the fix is to write the settings file only when there are some changes.  If a device connected to the same AP and there is no change in the AP, the settings file will never be written again after it got connected ok once.

The other fix about the stats is to allow user to put the "data" and "history" files to a non-volatile memory(RAM) to protect the flash.





On Wed, Jun 1, 2016 at 4:51 PM, Feng Wang <wangfe@nestlabs.com> wrote:
Minimize writes to the settings file such that there is only a write
when the data to br written has changed relative to the existed file.
---
 src/storage.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 72 insertions(+), 5 deletions(-)

diff --git a/src/storage.c b/src/storage.c
index 7d03130..d78a045 100644
--- a/src/storage.c
+++ b/src/storage.c
@@ -58,6 +58,71 @@ static GKeyFile *storage_load(const char *pathname)
        return keyfile;
 }

+static bool storage_changed(GKeyFile *keyfile, char *pathname, gchar *data, int length)
+{
+       GKeyFile *oldkeyfile = NULL;
+       gchar *olddata = NULL;
+       gsize oldlen = 0;
+       char id[100];
+       char *str, **strlist;
+       bool changed = false;
+
+       /* Get the service identifier pathname = "STORAGEDIR/id/settings" */
+       strcpy(id, pathname + strlen(STORAGEDIR) + 1);
+       str = strstr(id, SETTINGS);
+       if (str)
+               *(str-1) = 0;
+
+       /* Get existed configuration data on the disk */
+       oldkeyfile = storage_load(pathname);
+       if (oldkeyfile) {
+               /* Don't compare Domains if it is not in the current configuration */
+               str = g_key_file_get_string(keyfile, id, "Domains", NULL);
+               if (!str)
+                       g_key_file_remove_key(oldkeyfile, id, "Domains", NULL);
+               else
+                       g_free(str);
+
+               /* Set old Modified time same as current modified time */
+               str = g_key_file_get_string(keyfile, id, "Modified", NULL);
+               if (str) {
+                       g_key_file_set_string(oldkeyfile, id, "Modified", str);
+                       g_free(str);
+               } else
+                       g_key_file_remove_key(oldkeyfile, id, "Modified", NULL);
+
+               /* Don't compare DHCP configurations if not in the current configuration */
+               str = g_key_file_get_string(keyfile, id, "IPv4.DHCP.LastAddress", NULL);
+               if (!str)
+                       g_key_file_remove_key(oldkeyfile, id, "IPv4.DHCP.LastAddress", NULL);
+               else
+                       g_free(str);
+
+               str = g_key_file_get_string(keyfile, id, "IPv6.DHCP.LastAddress", NULL);
+               if (!str)
+                       g_key_file_remove_key(oldkeyfile, id, "IPv6.DHCP.LastAddress", NULL);
+               else
+                       g_free(str);
+
+               strlist = g_key_file_get_string_list(keyfile, id, "IPv6.DHCP.LastPrefixes",
+                                                       &oldlen, NULL);
+               if (!strlist)
+                       g_key_file_remove_key(oldkeyfile, id, "IPv6.DHCP.LastPrefixes", NULL);
+               else
+                       g_strfreev(strlist);
+
+               olddata = g_key_file_to_data(oldkeyfile, &oldlen, NULL);
+               g_key_file_free(oldkeyfile);
+       }
+
+       if (oldlen != length || memcmp(data, olddata, length))
+               changed = true;
+
+       g_free(olddata);
+
+       return changed;
+}
+
 static int storage_save(GKeyFile *keyfile, char *pathname)
 {
        gchar *data = NULL;
@@ -66,11 +131,13 @@ static int storage_save(GKeyFile *keyfile, char *pathname)
        int ret = 0;

        data = g_key_file_to_data(keyfile, &length, NULL);
-
-       if (!g_file_set_contents(pathname, data, length, &error)) {
-               DBG("Failed to store information: %s", error->message);
-               g_error_free(error);
-               ret = -EIO;
+       if (storage_changed(keyfile, pathname, data, length)) {
+               connman_info("storage_save file %s len %d", pathname, length);
+               if (!g_file_set_contents(pathname, data, length, &error)) {
+                       DBG("Failed to store information: %s", error->message);
+                       g_error_free(error);
+                       ret = -EIO;
+               }
        }

        g_free(data);
--
2.8.0.rc3.226.g39d4020