All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
@ 2009-07-05  3:14 Andrzej Zaborowski
  2009-07-14 20:54 ` Denis Kenzior
  0 siblings, 1 reply; 13+ messages in thread
From: Andrzej Zaborowski @ 2009-07-05  3:14 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 9950 bytes --]

This uses plain files in /var/cache/ofono for storing contents of the
operator lists to avoid possibly numerous queries to the SIM on every
startup.  Files are indexed with IMSI.  I'm not 100% sure about the
autoconf magic.

Users need to rerun bootstrap-configure after applying this.
---
 bootstrap-configure |    3 +-
 configure.ac        |    5 +
 src/sim.c           |  233 +++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 233 insertions(+), 8 deletions(-)

diff --git a/bootstrap-configure b/bootstrap-configure
index 2519f22..2c4d9a2 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -10,4 +10,5 @@ fi
 		--prefix=/usr \
 		--mandir=/usr/share/man \
 		--sysconfdir=/etc \
-		--disable-datafiles
+		--disable-datafiles \
+		--localstatedir=/var
diff --git a/configure.ac b/configure.ac
index 2d16fae..dd1d7aa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -87,6 +87,11 @@ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
 
 AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
 
+eval "eval LOCALSTATE_DIR=$localstatedir"
+AC_SUBST(LOCALSTATE_DIR)
+AC_DEFINE_UNQUOTED(CONFIG_LOCALSTATEDIR, "$LOCALSTATE_DIR",
+		   [Define to the location where state is stored.])
+
 COMPILER_FLAGS
 
 AC_OUTPUT(Makefile gdbus/Makefile gatchat/Makefile gisi/Makefile
diff --git a/src/sim.c b/src/sim.c
index 13557bf..f7992c7 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -25,6 +25,10 @@
 
 #include <string.h>
 #include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include <dbus/dbus.h>
 #include <glib.h>
@@ -70,6 +74,219 @@ struct sim_manager_data {
 	int pnn_current;
 };
 
+static int create_dirs(const char *filename, const mode_t mode)
+{
+	struct stat st;
+	char *dir;
+	const char *prev, *next;
+	int err;
+
+	err = stat(filename, &st);
+	if (!err && S_ISREG(st.st_mode))
+		return 0;
+
+	dir = g_malloc(strlen(filename) + 1);
+	strcpy(dir, "/");
+
+	for (prev = filename; next = strchr(prev + 1, '/'); prev = next)
+		if (next > prev + 1) {
+			strncat(dir, prev + 1, next - prev);
+
+			if (mkdir(dir, mode) && errno != EEXIST) {
+				g_free(dir);
+				return -1;
+			}
+		}
+
+	g_free(dir);
+	return 0;
+}
+
+#define SIM_CACHE_MODE 0600
+#define SIM_CACHE_PATH CONFIG_LOCALSTATEDIR "/cache/ofono/%s/%04x"
+#define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 2 + imsilen)
+
+struct sim_cache_callback {
+	struct sim_manager_data *sim;
+	void *cb;
+	void *data;
+	int fileid;
+	int record;
+};
+
+static void read_file_info_cached_cb(const struct ofono_error *error,
+		int file_length, enum ofono_sim_file_structure structure,
+		int record_length, void *data)
+{
+	struct sim_cache_callback *cbs = data;
+	ofono_sim_file_info_cb_t cb = cbs->cb;
+	char *path;
+	char *imsi = cbs->sim->imsi;
+	unsigned char fileinfo[6];
+	int fd = -1, fileid, ret;
+
+	cb(error, file_length, structure, record_length, cbs->data);
+
+	fileid = cbs->fileid;
+	g_free(cbs);
+
+	/* Even if the file doesn't exist, cache this fact so we don't
+	 * try and fail every time.  */
+	if (imsi) {
+		path = g_strdup_printf(SIM_CACHE_PATH ".info", imsi, fileid);
+		if (create_dirs(path, SIM_CACHE_MODE | S_IXUSR) == 0)
+			fd = open(path, O_WRONLY | O_CREAT, SIM_CACHE_MODE);
+		g_free(path);
+
+		if (fd == -1) {
+			ofono_debug("Error %i creating cache file for "
+					"fileid %04x, IMSI %s",
+					errno, fileid, imsi);
+			return;
+		}
+
+		fileinfo[0] = error->type;
+		fileinfo[1] = file_length >> 8;
+		fileinfo[2] = file_length & 0xff;
+		fileinfo[3] = structure;
+		fileinfo[4] = record_length >> 8;
+		fileinfo[5] = record_length & 0xff;
+
+		write(fd, fileinfo, 6);
+		close(fd);
+	}
+}
+
+static void read_file_info_cached(struct ofono_modem *modem, int fileid,
+		ofono_sim_file_info_cb_t cb, void *data)
+{
+	char *path;
+	char *imsi = modem->sim_manager->imsi;
+	int fd;
+	unsigned char fileinfo[6];
+	ssize_t len;
+	struct ofono_error error;
+	int file_length;
+	enum ofono_sim_file_structure structure;
+	int record_length;
+	struct sim_cache_callback *cbs;
+
+	if (!imsi)
+		goto uncached;
+
+	path = g_strdup_printf(SIM_CACHE_PATH ".info", imsi, fileid);
+	fd = open(path, O_RDONLY);
+	g_free(path);
+
+	if (fd == -1) {
+		if (errno != ENOENT)
+			ofono_debug("Error %i opening cache file for "
+					"fileid %04x, IMSI %s",
+					errno, fileid, imsi);
+
+		goto uncached;
+	}
+
+	len = read(fd, fileinfo, 6);
+	close(fd);
+
+	if (len == 6) {
+		error.type = fileinfo[0];
+		file_length = (fileinfo[1] << 8) | fileinfo[2];
+		structure = fileinfo[3];
+		record_length = (fileinfo[4] << 8) | fileinfo[5];
+
+		return cb(&error, file_length, structure, record_length, data);
+	}
+
+uncached:
+	cbs = g_new(struct sim_cache_callback, 1);
+	cbs->sim = modem->sim_manager;
+	cbs->cb = cb;
+	cbs->data = data;
+	cbs->fileid = fileid;
+	return modem->sim_manager->ops->read_file_info(modem,
+			fileid, read_file_info_cached_cb, cbs);
+}
+
+static void read_file_linear_cached_cb(const struct ofono_error *error,
+		const unsigned char *buffer, int length, void *data)
+{
+	struct sim_cache_callback *cbs = data;
+	ofono_sim_read_cb_t cb = cbs->cb;
+	char *path;
+	char *imsi = cbs->sim->imsi;
+	int fd = -1, fileid, ret, record;
+
+	cb(error, buffer, length, cbs->data);
+
+	fileid = cbs->fileid;
+	record = cbs->record;
+	g_free(cbs);
+
+	/* If read was successful, store the record contents */
+	if (error->type == OFONO_ERROR_TYPE_NO_ERROR && imsi) {
+		path = g_strdup_printf(SIM_CACHE_PATH, imsi, fileid);
+		if (create_dirs(path, SIM_CACHE_MODE | S_IXUSR) == 0)
+			fd = open(path, O_WRONLY | O_CREAT, SIM_CACHE_MODE);
+		g_free(path);
+
+		if (fd == -1)
+			return;
+
+		if (!record || lseek(fd, length * record, SEEK_SET) !=
+				(off_t) -1)
+			write(fd, buffer, length);
+		close(fd);
+	}
+}
+
+static void read_file_linear_cached(struct ofono_modem *modem, int fileid,
+		int record, int length, ofono_sim_read_cb_t cb, void *data)
+{
+	char *path;
+	char *imsi = modem->sim_manager->imsi;
+	int fd;
+	unsigned char buffer[length];
+	ssize_t rlength = 0;
+	struct ofono_error error;
+	struct sim_cache_callback *cbs;
+
+	if (!imsi)
+		goto uncached;
+
+	path = g_strdup_printf(SIM_CACHE_PATH, imsi, fileid);
+	fd = open(path, O_RDONLY);
+	g_free(path);
+
+	if (fd == -1) {
+		if (errno != ENOENT)
+			ofono_debug("Error %i opening cache file for "
+					"fileid %04x, IMSI %s", fileid, imsi);
+
+		goto uncached;
+	}
+
+	if (!record || lseek(fd, length * record, SEEK_SET) != (off_t) -1)
+		rlength = read(fd, buffer, length);
+	close(fd);
+
+	if (rlength == length) {
+		error.type = OFONO_ERROR_TYPE_NO_ERROR;
+		return cb(&error, buffer, length, data);
+	}
+
+uncached:
+	cbs = g_new(struct sim_cache_callback, 1);
+	cbs->sim = modem->sim_manager;
+	cbs->cb = cb;
+	cbs->data = data;
+	cbs->fileid = fileid;
+	cbs->record = record;
+	return modem->sim_manager->ops->read_file_linear(modem, fileid,
+			record, length, read_file_linear_cached_cb, cbs);
+}
+
 static char **get_own_numbers(GSList *own_numbers)
 {
 	int nelem = 0;
@@ -762,7 +979,7 @@ static void sim_opl_read_cb(const struct ofono_error *error,
 skip:
 	sim->opl_current ++;
 	if (sim->opl_current < sim->opl_num)
-		sim->ops->read_file_linear(modem, SIM_EFOPL_FILEID,
+		read_file_linear_cached(modem, SIM_EFOPL_FILEID,
 						sim->opl_current,
 						sim->opl_size,
 						sim_opl_read_cb, modem);
@@ -787,7 +1004,7 @@ static void sim_opl_info_cb(const struct ofono_error *error, int length,
 	sim->opl_current = 0;
 	sim->opl_size = record_length;
 	sim->opl_num = length / record_length;
-	sim->ops->read_file_linear(modem, SIM_EFOPL_FILEID, 0,
+	read_file_linear_cached(modem, SIM_EFOPL_FILEID, 0,
 			record_length, sim_opl_read_cb, modem);
 }
 
@@ -796,7 +1013,7 @@ static gboolean sim_retrieve_opl(void *user_data)
 	struct ofono_modem *modem = user_data;
 	struct sim_manager_data *sim = modem->sim_manager;
 
-	sim->ops->read_file_info(modem, SIM_EFOPL_FILEID,
+	read_file_info_cached(modem, SIM_EFOPL_FILEID,
 			sim_opl_info_cb, modem);
 
 	return FALSE;
@@ -861,7 +1078,7 @@ static void sim_pnn_read_cb(const struct ofono_error *error,
 skip:
 	sim->pnn_current ++;
 	if (sim->pnn_current < sim->pnn_num)
-		sim->ops->read_file_linear(modem, SIM_EFPNN_FILEID,
+		read_file_linear_cached(modem, SIM_EFPNN_FILEID,
 						sim->pnn_current,
 						sim->pnn_size,
 						sim_pnn_read_cb, modem);
@@ -892,7 +1109,7 @@ static void sim_pnn_info_cb(const struct ofono_error *error, int length,
 	sim->pnn_size = record_length;
 	sim->pnn_num = length / record_length;
 	sim->pnn = g_new0(struct pnn_operator, sim->pnn_num);
-	sim->ops->read_file_linear(modem, SIM_EFPNN_FILEID, 0,
+	read_file_linear_cached(modem, SIM_EFPNN_FILEID, 0,
 			record_length, sim_pnn_read_cb, modem);
 }
 
@@ -901,7 +1118,7 @@ static gboolean sim_retrieve_pnn(void *user_data)
 	struct ofono_modem *modem = user_data;
 	struct sim_manager_data *sim = modem->sim_manager;
 
-	sim->ops->read_file_info(modem, SIM_EFPNN_FILEID,
+	read_file_info_cached(modem, SIM_EFPNN_FILEID,
 			sim_pnn_info_cb, modem);
 
 	return FALSE;
@@ -940,8 +1157,10 @@ static void initialize_sim_manager(struct ofono_modem *modem)
 	if (modem->sim_manager->ops->read_file_transparent)
 		g_timeout_add(0, sim_retrieve_spdi, modem);
 
+	/* Schedule later than IMSI so that there's a chance we can
+	 * use SIM cache, which is IMSI-indexed.  */
 	if (modem->sim_manager->ops->read_file_linear)
-		g_timeout_add(0, sim_retrieve_pnn, modem);
+		g_timeout_add_seconds(1, sim_retrieve_pnn, modem);
 }
 
 int ofono_sim_manager_register(struct ofono_modem *modem,
-- 
1.6.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-05  3:14 [PATCH] Cache EF-PNN, EF-OPL sim files on disk Andrzej Zaborowski
@ 2009-07-14 20:54 ` Denis Kenzior
  2009-07-18  4:34   ` Andrzej Zaborowski
  0 siblings, 1 reply; 13+ messages in thread
From: Denis Kenzior @ 2009-07-14 20:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 425 bytes --]

> This uses plain files in /var/cache/ofono for storing contents of the
> operator lists to avoid possibly numerous queries to the SIM on every
> startup.  Files are indexed with IMSI.  I'm not 100% sure about the
> autoconf magic.
>
> Users need to rerun bootstrap-configure after applying this.

Can you rebase and resubmit this patch?  The changes to sim.c and network.c 
were a bit extensive.

Thanks,
-Denis

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-14 20:54 ` Denis Kenzior
@ 2009-07-18  4:34   ` Andrzej Zaborowski
  2009-07-18  6:02     ` Denis Kenzior
  0 siblings, 1 reply; 13+ messages in thread
From: Andrzej Zaborowski @ 2009-07-18  4:34 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 8137 bytes --]

This uses plain files in /var/cache/ofono for storing contents of the
operator lists to avoid possibly numerous queries to the SIM on every
startup.  Files are indexed with IMSI.  I'm not 100% sure about the
autoconf magic.

Users need to rerun bootstrap-configure after applying this.

File format slightly changed in this second version (make it as simple as
possible).
---
 bootstrap-configure |    3 +-
 configure.ac        |    5 ++
 src/sim.c           |  193 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 200 insertions(+), 1 deletions(-)

diff --git a/bootstrap-configure b/bootstrap-configure
index cc82e1a..cea17bf 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -12,4 +12,5 @@ fi
 		--prefix=/usr \
 		--mandir=/usr/share/man \
 		--sysconfdir=/etc \
-		--disable-datafiles
+		--disable-datafiles \
+		--localstatedir=/var
diff --git a/configure.ac b/configure.ac
index 640c3cd..a7cf947 100644
--- a/configure.ac
+++ b/configure.ac
@@ -96,6 +96,11 @@ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
 
 AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
 
+eval "eval LOCALSTATE_DIR=$localstatedir"
+AC_SUBST(LOCALSTATE_DIR)
+AC_DEFINE_UNQUOTED(CONFIG_LOCALSTATEDIR, "$LOCALSTATE_DIR",
+		   [Define to the location where state is stored.])
+
 COMPILER_FLAGS
 
 AC_OUTPUT(Makefile gdbus/Makefile gatchat/Makefile gisi/Makefile
diff --git a/src/sim.c b/src/sim.c
index 86f2e98..e3590ba 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -29,6 +29,10 @@
 #include <dbus/dbus.h>
 #include <glib.h>
 #include <gdbus.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include "ofono.h"
 
@@ -267,6 +271,34 @@ static gboolean sim_retrieve_imsi(void *user_data)
 	return FALSE;
 }
 
+static int create_dirs(const char *filename, const mode_t mode)
+{
+	struct stat st;
+	char *dir;
+	const char *prev, *next;
+	int err;
+
+	err = stat(filename, &st);
+	if (!err && S_ISREG(st.st_mode))
+		return 0;
+
+	dir = g_malloc(strlen(filename) + 1);
+	strcpy(dir, "/");
+
+	for (prev = filename; next = strchr(prev + 1, '/'); prev = next)
+		if (next > prev + 1) {
+			strncat(dir, prev + 1, next - prev);
+
+			if (mkdir(dir, mode) && errno != EEXIST) {
+				g_free(dir);
+				return -1;
+			}
+		}
+
+	g_free(dir);
+	return 0;
+}
+
 static void sim_op_error(struct ofono_modem *modem)
 {
 	struct sim_manager_data *sim = modem->sim_manager;
@@ -280,6 +312,11 @@ static void sim_op_error(struct ofono_modem *modem)
 		g_timeout_add(0, sim_op_next, modem);
 }
 
+#define SIM_CACHE_MODE 0600
+#define SIM_CACHE_PATH CONFIG_LOCALSTATEDIR "/cache/ofono/%s/%04x"
+#define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 2 + imsilen)
+#define SIM_CACHE_HEADER_SIZE 6
+
 static void sim_op_retrieve_cb(const struct ofono_error *error,
 				const unsigned char *data, int len, void *user)
 {
@@ -288,6 +325,10 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
 	struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
 	int total = op->length / op->record_length;
 
+	char *imsi = sim->imsi;
+	char *path;
+	int fd, ret;
+
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		if (op->current == 1)
 			sim_op_error(modem);
@@ -298,6 +339,23 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
 	op->cb(modem, 1, op->structure, op->length, op->current,
 		data, op->record_length, op->userdata);
 
+	if (imsi) {
+		/* Cache the record */
+		path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
+		fd = open(path, O_WRONLY);
+		g_free(path);
+
+		if (fd == -1)
+			goto next;
+
+		if (lseek(fd, (op->current - 1) * op->record_length +
+					SIM_CACHE_HEADER_SIZE, SEEK_SET) !=
+				(off_t) -1)
+			write(fd, data, op->record_length);
+		close(fd);
+	}
+
+next:
 	if (op->current == total) {
 		op = g_queue_pop_head(sim->simop_q);
 
@@ -362,6 +420,38 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
 	struct sim_manager_data *sim = modem->sim_manager;
 	struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
 
+	char *imsi = sim->imsi;
+	char *path;
+	unsigned char fileinfo[6];
+	int fd = -1;
+
+	/* Even if the file doesn't exist, cache this fact so we don't
+	 * try and fail every time.  */
+	if (imsi) {
+		path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
+		if (create_dirs(path, SIM_CACHE_MODE | S_IXUSR) == 0)
+			fd = open(path, O_WRONLY | O_CREAT, SIM_CACHE_MODE);
+		g_free(path);
+
+		if (fd == -1) {
+			ofono_debug("Error %i creating cache file for "
+					"fileid %04x, IMSI %s",
+					errno, op->id, imsi);
+			goto process_result;
+		}
+
+		fileinfo[0] = error->type;
+		fileinfo[1] = length >> 8;
+		fileinfo[2] = length & 0xff;
+		fileinfo[3] = structure;
+		fileinfo[4] = record_length >> 8;
+		fileinfo[5] = record_length & 0xff;
+
+		write(fd, fileinfo, 6);
+		close(fd);
+	}
+
+process_result:
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		sim_op_error(modem);
 		return;
@@ -396,6 +486,106 @@ static gboolean sim_op_next(gpointer user_data)
 	return FALSE;
 }
 
+struct sim_cache_callback {
+	ofono_sim_file_read_cb_t cb;
+	void *userdata;
+	struct ofono_modem *modem;
+	int error;
+	int fd;
+	enum ofono_sim_file_structure structure;
+	unsigned int record_length;
+	unsigned int total;
+};
+
+static gboolean sim_op_cached_callback(gpointer user)
+{
+	struct sim_cache_callback *cbs = user;
+	guint8 buffer[cbs->record_length];
+	unsigned int record;
+
+	if (cbs->error != OFONO_ERROR_TYPE_NO_ERROR) {
+		cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
+		goto cleanup;
+	}
+
+	for (record = 0; record < cbs->total; record++) {
+		if (read(cbs->fd, buffer, cbs->record_length) <
+				(int) cbs->record_length) {
+			cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
+			break;
+		}
+
+		cbs->cb(cbs->modem, 1, cbs->structure,
+				cbs->record_length * cbs->total, record + 1,
+				buffer, cbs->record_length, cbs->userdata);
+	}
+
+cleanup:
+	close(cbs->fd);
+	g_free(cbs);
+
+	return FALSE;
+}
+
+static gboolean sim_op_check_cached(struct ofono_modem *modem, int fileid,
+			ofono_sim_file_read_cb_t cb, void *data)
+{
+	struct sim_manager_data *sim = modem->sim_manager;
+	char *imsi = sim->imsi;
+	char *path;
+	int fd;
+	unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
+	ssize_t len;
+	struct ofono_error error;
+	unsigned int file_length;
+	enum ofono_sim_file_structure structure;
+	unsigned int record_length;
+	struct sim_cache_callback *cbs;
+
+	if (!imsi)
+		return FALSE;
+
+	path = g_strdup_printf(SIM_CACHE_PATH, imsi, fileid);
+	fd = open(path, O_RDONLY);
+	g_free(path);
+
+	if (fd == -1) {
+		if (errno != ENOENT)
+			ofono_debug("Error %i opening cache file for "
+					"fileid %04x, IMSI %s",
+					errno, fileid, imsi);
+
+		return FALSE;
+	}
+
+	len = read(fd, fileinfo, SIM_CACHE_HEADER_SIZE);
+	if (len != SIM_CACHE_HEADER_SIZE)
+		return FALSE;
+
+	error.type = fileinfo[0];
+	file_length = (fileinfo[1] << 8) | fileinfo[2];
+	structure = fileinfo[3];
+	record_length = (fileinfo[4] << 8) | fileinfo[5];
+
+	if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
+		record_length = file_length;
+	if (record_length == 0 || file_length < record_length)
+		return FALSE;
+
+	cbs = g_new(struct sim_cache_callback, 1);
+	cbs->cb = cb;
+	cbs->userdata = data;
+	cbs->modem = modem;
+	cbs->error = error.type;
+	cbs->fd = fd;
+	cbs->structure = structure;
+	cbs->record_length = record_length;
+	cbs->total = file_length / record_length;
+	g_timeout_add(0, sim_op_cached_callback, cbs);
+
+	return TRUE;
+}
+
 int ofono_sim_read(struct ofono_modem *modem, int id,
 			ofono_sim_file_read_cb_t cb, void *data)
 {
@@ -408,6 +598,9 @@ int ofono_sim_read(struct ofono_modem *modem, int id,
 	if (modem->sim_manager == NULL)
 		return -1;
 
+	if (sim_op_check_cached(modem, id, cb, data))
+		return 0;
+
 	if (!sim->ops)
 		return -1;
 
-- 
1.6.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-18  4:34   ` Andrzej Zaborowski
@ 2009-07-18  6:02     ` Denis Kenzior
  2009-07-18 11:32       ` andrzej zaborowski
  0 siblings, 1 reply; 13+ messages in thread
From: Denis Kenzior @ 2009-07-18  6:02 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 1370 bytes --]

Hi,

> This uses plain files in /var/cache/ofono for storing contents of the
> operator lists to avoid possibly numerous queries to the SIM on every
> startup.  Files are indexed with IMSI.  I'm not 100% sure about the
> autoconf magic.
>
> Users need to rerun bootstrap-configure after applying this.
>
> File format slightly changed in this second version (make it as simple as
> possible).
>  		return -1;

I somewhat like the idea of transparently caching the SIM file system, 
unfortunately we can't do it for all files.  Suppose a user shuts off oFono 
device, takes out the SIM and puts it in another device.  Then modifies some 
settings (e.g. like MBDN or MSISDN.)  Then the SIM is returned to oFono based 
device.

However, not all files can be updated by the user.  Namely files with the 'ADM' 
for UPDATE access condition.  The only way they can be changed is at a service 
center.

What do you think of transparently caching only such files?  This would include 
the ones we're really interested in caching anyway, namely EFspn, EFpnn, 
EFopl, EFust, etc.

If we decide to do this, we should store them in /var/lib/ instead of 
/var/cache, since these files will be stored semi-permanently.  If really 
necessary, the carrier service center could probably remove the cache somehow 
during the SIM upgrade.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-18  6:02     ` Denis Kenzior
@ 2009-07-18 11:32       ` andrzej zaborowski
  2009-07-18 11:39         ` Marcel Holtmann
  2009-07-18 12:42         ` andrzej zaborowski
  0 siblings, 2 replies; 13+ messages in thread
From: andrzej zaborowski @ 2009-07-18 11:32 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 1121 bytes --]

2009/7/18 Denis Kenzior <denkenz@gmail.com>:
> However, not all files can be updated by the user.  Namely files with the 'ADM'
> for UPDATE access condition.  The only way they can be changed is at a service
> center.
>
> What do you think of transparently caching only such files?  This would include
> the ones we're really interested in caching anyway, namely EFspn, EFpnn,
> EFopl, EFust, etc.

True, I was going to add code or a TODO in ofono_sim_write() to
invalidate cache but it's a good point about updating outside ofono
control.  I now added a parameter to ofono_sim_read() to indicate that
a file can be cached on disk, making it a little less transparent.  In
previous version I had separate entry points for cached read.

>
> If we decide to do this, we should store them in /var/lib/ instead of
> /var/cache, since these files will be stored semi-permanently.

Ok, changed.  There's also a standard path /var/state, I don't know if
it's better or worse.

I should note that the cached reads can change the callback order
because they will skip simop_q queue in sim.c.

Regards

[-- Attachment #2: 0001-Cache-EF-PNN-EF-OPL-sim-files-on-disk.patch --]
[-- Type: application/octet-stream, Size: 11125 bytes --]

From 9852f953a647d3e32a0637153d00096d4ac6942a Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Sat, 18 Jul 2009 15:19:51 +0200
Subject: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.

This uses plain files in /var/cache/ofono for storing contents of the
operator lists to avoid possibly numerous queries to the SIM on every
startup.  Files are indexed with IMSI.  I'm not 100% sure about the
autoconf magic.

Users need to rerun bootstrap-configure after applying this.

File format slightly changed in this second version (make it as simple as
possible).
---
 bootstrap-configure |    3 +-
 configure.ac        |    5 ++
 src/network.c       |    9 ++-
 src/sim.c           |  200 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/sim.h           |    5 +-
 5 files changed, 214 insertions(+), 8 deletions(-)

diff --git a/bootstrap-configure b/bootstrap-configure
index cc82e1a..cea17bf 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -12,4 +12,5 @@ fi
 		--prefix=/usr \
 		--mandir=/usr/share/man \
 		--sysconfdir=/etc \
-		--disable-datafiles
+		--disable-datafiles \
+		--localstatedir=/var
diff --git a/configure.ac b/configure.ac
index 640c3cd..a7cf947 100644
--- a/configure.ac
+++ b/configure.ac
@@ -96,6 +96,11 @@ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
 
 AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
 
+eval "eval LOCALSTATE_DIR=$localstatedir"
+AC_SUBST(LOCALSTATE_DIR)
+AC_DEFINE_UNQUOTED(CONFIG_LOCALSTATEDIR, "$LOCALSTATE_DIR",
+		   [Define to the location where state is stored.])
+
 COMPILER_FLAGS
 
 AC_OUTPUT(Makefile gdbus/Makefile gatchat/Makefile gisi/Makefile
diff --git a/src/network.c b/src/network.c
index 1152b28..43a3a83 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1265,7 +1265,8 @@ static void sim_pnn_read_cb(struct ofono_modem *modem, int ok,
 	 * still be used for the HPLMN and/or EHPLMN, if PNN
 	 * is present.  */
 	if (record == total && !sim_eons_pnn_is_empty(netreg->eons))
-		ofono_sim_read(modem, SIM_EFOPL_FILEID, sim_opl_read_cb, NULL);
+		ofono_sim_read(modem, SIM_EFOPL_FILEID,
+				sim_opl_read_cb, NULL, 1);
 }
 
 static void sim_spdi_read_cb(struct ofono_modem *modem, int ok,
@@ -1351,7 +1352,7 @@ static void sim_spn_read_cb(struct ofono_modem *modem, int ok,
 	}
 
 	netreg->spname = spn;
-	ofono_sim_read(modem, SIM_EFSPDI_FILEID, sim_spdi_read_cb, NULL);
+	ofono_sim_read(modem, SIM_EFSPDI_FILEID, sim_spdi_read_cb, NULL, 1);
 
 	if (dcbyte & SIM_EFSPN_DC_HOME_PLMN_BIT)
 		netreg->flags |= NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN;
@@ -1374,8 +1375,8 @@ static void sim_spn_read_cb(struct ofono_modem *modem, int ok,
 
 static void network_sim_ready(struct ofono_modem *modem)
 {
-	ofono_sim_read(modem, SIM_EFPNN_FILEID, sim_pnn_read_cb, NULL);
-	ofono_sim_read(modem, SIM_EFSPN_FILEID, sim_spn_read_cb, NULL);
+	ofono_sim_read(modem, SIM_EFPNN_FILEID, sim_pnn_read_cb, NULL, 1);
+	ofono_sim_read(modem, SIM_EFSPN_FILEID, sim_spn_read_cb, NULL, 1);
 }
 
 int ofono_network_registration_register(struct ofono_modem *modem,
diff --git a/src/sim.c b/src/sim.c
index 86f2e98..404419b 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -29,6 +29,10 @@
 #include <dbus/dbus.h>
 #include <glib.h>
 #include <gdbus.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include "ofono.h"
 
@@ -48,6 +52,7 @@ static gboolean sim_op_retrieve_next(gpointer user);
 
 struct sim_file_op {
 	int id;
+	int cache;
 	enum ofono_sim_file_structure structure;
 	int length;
 	int record_length;
@@ -232,7 +237,8 @@ check:
 
 static void sim_ready(struct ofono_modem *modem)
 {
-	ofono_sim_read(modem, SIM_EFMSISDN_FILEID, sim_msisdn_read_cb, NULL);
+	ofono_sim_read(modem, SIM_EFMSISDN_FILEID,
+			sim_msisdn_read_cb, NULL, 0);
 }
 
 static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,
@@ -267,6 +273,34 @@ static gboolean sim_retrieve_imsi(void *user_data)
 	return FALSE;
 }
 
+static int create_dirs(const char *filename, const mode_t mode)
+{
+	struct stat st;
+	char *dir;
+	const char *prev, *next;
+	int err;
+
+	err = stat(filename, &st);
+	if (!err && S_ISREG(st.st_mode))
+		return 0;
+
+	dir = g_malloc(strlen(filename) + 1);
+	strcpy(dir, "/");
+
+	for (prev = filename; next = strchr(prev + 1, '/'); prev = next)
+		if (next > prev + 1) {
+			strncat(dir, prev + 1, next - prev);
+
+			if (mkdir(dir, mode) && errno != EEXIST) {
+				g_free(dir);
+				return -1;
+			}
+		}
+
+	g_free(dir);
+	return 0;
+}
+
 static void sim_op_error(struct ofono_modem *modem)
 {
 	struct sim_manager_data *sim = modem->sim_manager;
@@ -280,6 +314,11 @@ static void sim_op_error(struct ofono_modem *modem)
 		g_timeout_add(0, sim_op_next, modem);
 }
 
+#define SIM_CACHE_MODE 0600
+#define SIM_CACHE_PATH CONFIG_LOCALSTATEDIR "/lib/ofono/%s/%04x"
+#define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 2 + imsilen)
+#define SIM_CACHE_HEADER_SIZE 6
+
 static void sim_op_retrieve_cb(const struct ofono_error *error,
 				const unsigned char *data, int len, void *user)
 {
@@ -288,6 +327,10 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
 	struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
 	int total = op->length / op->record_length;
 
+	char *imsi = sim->imsi;
+	char *path;
+	int fd, ret;
+
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		if (op->current == 1)
 			sim_op_error(modem);
@@ -298,6 +341,23 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
 	op->cb(modem, 1, op->structure, op->length, op->current,
 		data, op->record_length, op->userdata);
 
+	if (op->cache && imsi) {
+		/* Cache the record */
+		path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
+		fd = open(path, O_WRONLY);
+		g_free(path);
+
+		if (fd == -1)
+			goto next;
+
+		if (lseek(fd, (op->current - 1) * op->record_length +
+					SIM_CACHE_HEADER_SIZE, SEEK_SET) !=
+				(off_t) -1)
+			write(fd, data, op->record_length);
+		close(fd);
+	}
+
+next:
 	if (op->current == total) {
 		op = g_queue_pop_head(sim->simop_q);
 
@@ -362,6 +422,38 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
 	struct sim_manager_data *sim = modem->sim_manager;
 	struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
 
+	char *imsi = sim->imsi;
+	char *path;
+	unsigned char fileinfo[6];
+	int fd = -1;
+
+	if (op->cache && imsi) {
+		/* Even if the file doesn't exist, cache this fact so we don't
+		 * try and fail every time.  */
+		path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
+		if (create_dirs(path, SIM_CACHE_MODE | S_IXUSR) == 0)
+			fd = open(path, O_WRONLY | O_CREAT, SIM_CACHE_MODE);
+		g_free(path);
+
+		if (fd == -1) {
+			ofono_debug("Error %i creating cache file for "
+					"fileid %04x, IMSI %s",
+					errno, op->id, imsi);
+			goto process_result;
+		}
+
+		fileinfo[0] = error->type;
+		fileinfo[1] = length >> 8;
+		fileinfo[2] = length & 0xff;
+		fileinfo[3] = structure;
+		fileinfo[4] = record_length >> 8;
+		fileinfo[5] = record_length & 0xff;
+
+		write(fd, fileinfo, 6);
+		close(fd);
+	}
+
+process_result:
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		sim_op_error(modem);
 		return;
@@ -396,10 +488,110 @@ static gboolean sim_op_next(gpointer user_data)
 	return FALSE;
 }
 
-int ofono_sim_read(struct ofono_modem *modem, int id,
+struct sim_cache_callback {
+	ofono_sim_file_read_cb_t cb;
+	void *userdata;
+	struct ofono_modem *modem;
+	int error;
+	int fd;
+	enum ofono_sim_file_structure structure;
+	unsigned int record_length;
+	unsigned int total;
+};
+
+static gboolean sim_op_cached_callback(gpointer user)
+{
+	struct sim_cache_callback *cbs = user;
+	guint8 buffer[cbs->record_length];
+	unsigned int record;
+
+	if (cbs->error != OFONO_ERROR_TYPE_NO_ERROR) {
+		cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
+		goto cleanup;
+	}
+
+	for (record = 0; record < cbs->total; record++) {
+		if (read(cbs->fd, buffer, cbs->record_length) <
+				(int) cbs->record_length) {
+			cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
+			break;
+		}
+
+		cbs->cb(cbs->modem, 1, cbs->structure,
+				cbs->record_length * cbs->total, record + 1,
+				buffer, cbs->record_length, cbs->userdata);
+	}
+
+cleanup:
+	close(cbs->fd);
+	g_free(cbs);
+
+	return FALSE;
+}
+
+static gboolean sim_op_check_cached(struct ofono_modem *modem, int fileid,
 			ofono_sim_file_read_cb_t cb, void *data)
 {
 	struct sim_manager_data *sim = modem->sim_manager;
+	char *imsi = sim->imsi;
+	char *path;
+	int fd;
+	unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
+	ssize_t len;
+	struct ofono_error error;
+	unsigned int file_length;
+	enum ofono_sim_file_structure structure;
+	unsigned int record_length;
+	struct sim_cache_callback *cbs;
+
+	if (!imsi)
+		return FALSE;
+
+	path = g_strdup_printf(SIM_CACHE_PATH, imsi, fileid);
+	fd = open(path, O_RDONLY);
+	g_free(path);
+
+	if (fd == -1) {
+		if (errno != ENOENT)
+			ofono_debug("Error %i opening cache file for "
+					"fileid %04x, IMSI %s",
+					errno, fileid, imsi);
+
+		return FALSE;
+	}
+
+	len = read(fd, fileinfo, SIM_CACHE_HEADER_SIZE);
+	if (len != SIM_CACHE_HEADER_SIZE)
+		return FALSE;
+
+	error.type = fileinfo[0];
+	file_length = (fileinfo[1] << 8) | fileinfo[2];
+	structure = fileinfo[3];
+	record_length = (fileinfo[4] << 8) | fileinfo[5];
+
+	if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
+		record_length = file_length;
+	if (record_length == 0 || file_length < record_length)
+		return FALSE;
+
+	cbs = g_new(struct sim_cache_callback, 1);
+	cbs->cb = cb;
+	cbs->userdata = data;
+	cbs->modem = modem;
+	cbs->error = error.type;
+	cbs->fd = fd;
+	cbs->structure = structure;
+	cbs->record_length = record_length;
+	cbs->total = file_length / record_length;
+	g_timeout_add(0, sim_op_cached_callback, cbs);
+
+	return TRUE;
+}
+
+int ofono_sim_read(struct ofono_modem *modem, int id,
+			ofono_sim_file_read_cb_t cb, void *data, int can_cache)
+{
+	struct sim_manager_data *sim = modem->sim_manager;
 	struct sim_file_op *op;
 
 	if (!cb)
@@ -408,6 +600,9 @@ int ofono_sim_read(struct ofono_modem *modem, int id,
 	if (modem->sim_manager == NULL)
 		return -1;
 
+	if (can_cache && sim_op_check_cached(modem, id, cb, data))
+		return 0;
+
 	if (!sim->ops)
 		return -1;
 
@@ -426,6 +621,7 @@ int ofono_sim_read(struct ofono_modem *modem, int id,
 	op->id = id;
 	op->cb = cb;
 	op->userdata = data;
+	op->cache = can_cache;
 
 	g_queue_push_tail(sim->simop_q, op);
 
diff --git a/src/sim.h b/src/sim.h
index 8e7870d..b50310c 100644
--- a/src/sim.h
+++ b/src/sim.h
@@ -45,10 +45,13 @@ void ofono_sim_set_ready(struct ofono_modem *modem);
  * if an error has occurred.  For transparent files, the callback will only
  * be called once.
  *
+ * can_cache should only be set for files with update condition 'ADM'.
+ *
  * Returns 0 if the request could be queued, -1 otherwise.
  */
 int ofono_sim_read(struct ofono_modem *modem, int id,
-			ofono_sim_file_read_cb_t cb, void *data);
+			ofono_sim_file_read_cb_t cb, void *data,
+			int can_cache);
 
 int ofono_sim_write(struct ofono_modem *modem, int id,
 			enum ofono_sim_file_structure structure, int record,
-- 
1.6.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-18 11:32       ` andrzej zaborowski
@ 2009-07-18 11:39         ` Marcel Holtmann
  2009-07-18 12:42         ` andrzej zaborowski
  1 sibling, 0 replies; 13+ messages in thread
From: Marcel Holtmann @ 2009-07-18 11:39 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 1403 bytes --]

Hi Andrzej,

> > However, not all files can be updated by the user.  Namely files with the 'ADM'
> > for UPDATE access condition.  The only way they can be changed is at a service
> > center.
> >
> > What do you think of transparently caching only such files?  This would include
> > the ones we're really interested in caching anyway, namely EFspn, EFpnn,
> > EFopl, EFust, etc.
> 
> True, I was going to add code or a TODO in ofono_sim_write() to
> invalidate cache but it's a good point about updating outside ofono
> control.  I now added a parameter to ofono_sim_read() to indicate that
> a file can be cached on disk, making it a little less transparent.  In
> previous version I had separate entry points for cached read.
> 
> >
> > If we decide to do this, we should store them in /var/lib/ instead of
> > /var/cache, since these files will be stored semi-permanently.
> 
> Ok, changed.  There's also a standard path /var/state, I don't know if
> it's better or worse.

go with /var/lib since that is most common for persistent information.
You can use /var/cache for information that can be easily retrieved
again without having an impact on the functionality.

The /var/state should not be bothered with. If you have information that
are only stored for one session then /var/run should be used. However
that will be wiped during reboot.

Regards

Marcel



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-18 11:32       ` andrzej zaborowski
  2009-07-18 11:39         ` Marcel Holtmann
@ 2009-07-18 12:42         ` andrzej zaborowski
  2009-07-20 16:12           ` Andrzej Zaborowski
  1 sibling, 1 reply; 13+ messages in thread
From: andrzej zaborowski @ 2009-07-18 12:42 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 891 bytes --]

2009/7/18 andrzej zaborowski <balrogg@gmail.com>:
> 2009/7/18 Denis Kenzior <denkenz@gmail.com>:
>> However, not all files can be updated by the user.  Namely files with the 'ADM'
>> for UPDATE access condition.  The only way they can be changed is at a service
>> center.
>>
>> What do you think of transparently caching only such files?  This would include
>> the ones we're really interested in caching anyway, namely EFspn, EFpnn,
>> EFopl, EFust, etc.
>
> True, I was going to add code or a TODO in ofono_sim_write() to
> invalidate cache but it's a good point about updating outside ofono
> control.  I now added a parameter to ofono_sim_read() to indicate that
> a file can be cached on disk, making it a little less transparent.

Actually, ignore this, the response to SELECT gives access conditions
so we can use that.  I'll fix it and resend later.

Regards

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-20 16:12           ` Andrzej Zaborowski
@ 2009-07-20 14:24             ` Marcel Holtmann
  2009-07-20 14:56               ` andrzej zaborowski
  2009-07-27 23:13             ` Denis Kenzior
  1 sibling, 1 reply; 13+ messages in thread
From: Marcel Holtmann @ 2009-07-20 14:24 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 480 bytes --]

Hi Andrzej,

> This uses plain files in /var/cache/ofono for storing contents of the
> operator lists to avoid possibly numerous queries to the SIM on every
> startup.  Files are indexed with IMSI.  I'm not 100% sure about the
> autoconf magic.

please have a look into BlueZ or ConnMan and use STORAGEDIR for the base
path of /var/lib/ofono.

Can we just use GKeyFile to store these files? It would make reading and
adding them a lot easier.

Regards

Marcel



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-20 14:24             ` Marcel Holtmann
@ 2009-07-20 14:56               ` andrzej zaborowski
  0 siblings, 0 replies; 13+ messages in thread
From: andrzej zaborowski @ 2009-07-20 14:56 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 1178 bytes --]

2009/7/20 Marcel Holtmann <marcel@holtmann.org>:
>> This uses plain files in /var/cache/ofono for storing contents of the
>> operator lists to avoid possibly numerous queries to the SIM on every
>> startup.  Files are indexed with IMSI.  I'm not 100% sure about the
>> autoconf magic.
>
> please have a look into BlueZ or ConnMan and use STORAGEDIR for the base
> path of /var/lib/ofono.

Okay.

>
> Can we just use GKeyFile to store these files? It would make reading and
> adding them a lot easier.

I didn't know about GKeyFile but I considered BlueZ textfile after
Denis suggested them.  The values in GKeyFile are UTF-8 encoded with
some characters reserved so we would need either very heavy escaping
or storing the SIM file contents hex encoded.  Keys would be only
perhaps "FileInfo" and "Record1"..."RecordN" because SIM files are
nothing like key-value pairs.  If you think the reading convenience is
worth the parsing overhead, then I can change the patch to use
GKeyFile or json or xml.

Supposedly this is only a form caching so there shouldn't be need for
reading or adding these files, they can get overwritten at any time.

Regards

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-18 12:42         ` andrzej zaborowski
@ 2009-07-20 16:12           ` Andrzej Zaborowski
  2009-07-20 14:24             ` Marcel Holtmann
  2009-07-27 23:13             ` Denis Kenzior
  0 siblings, 2 replies; 13+ messages in thread
From: Andrzej Zaborowski @ 2009-07-20 16:12 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 8919 bytes --]

This uses plain files in /var/cache/ofono for storing contents of the
operator lists to avoid possibly numerous queries to the SIM on every
startup.  Files are indexed with IMSI.  I'm not 100% sure about the
autoconf magic.

Users need to rerun bootstrap-configure after applying this.

Depends on [PATCH] Return SIM file access conditions from read_file_info.
---
 bootstrap-configure |    3 +-
 configure.ac        |    5 ++
 src/sim.c           |  197 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 204 insertions(+), 1 deletions(-)

diff --git a/bootstrap-configure b/bootstrap-configure
index cc82e1a..cea17bf 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -12,4 +12,5 @@ fi
 		--prefix=/usr \
 		--mandir=/usr/share/man \
 		--sysconfdir=/etc \
-		--disable-datafiles
+		--disable-datafiles \
+		--localstatedir=/var
diff --git a/configure.ac b/configure.ac
index 640c3cd..a7cf947 100644
--- a/configure.ac
+++ b/configure.ac
@@ -96,6 +96,11 @@ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
 
 AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
 
+eval "eval LOCALSTATE_DIR=$localstatedir"
+AC_SUBST(LOCALSTATE_DIR)
+AC_DEFINE_UNQUOTED(CONFIG_LOCALSTATEDIR, "$LOCALSTATE_DIR",
+		   [Define to the location where state is stored.])
+
 COMPILER_FLAGS
 
 AC_OUTPUT(Makefile gdbus/Makefile gatchat/Makefile gisi/Makefile
diff --git a/src/sim.c b/src/sim.c
index 787ebc7..7f905b1 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -29,6 +29,10 @@
 #include <dbus/dbus.h>
 #include <glib.h>
 #include <gdbus.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include "ofono.h"
 
@@ -48,6 +52,7 @@ static gboolean sim_op_retrieve_next(gpointer user);
 
 struct sim_file_op {
 	int id;
+	int cache;
 	enum ofono_sim_file_structure structure;
 	int length;
 	int record_length;
@@ -267,6 +272,34 @@ static gboolean sim_retrieve_imsi(void *user_data)
 	return FALSE;
 }
 
+static int create_dirs(const char *filename, const mode_t mode)
+{
+	struct stat st;
+	char *dir;
+	const char *prev, *next;
+	int err;
+
+	err = stat(filename, &st);
+	if (!err && S_ISREG(st.st_mode))
+		return 0;
+
+	dir = g_malloc(strlen(filename) + 1);
+	strcpy(dir, "/");
+
+	for (prev = filename; next = strchr(prev + 1, '/'); prev = next)
+		if (next > prev + 1) {
+			strncat(dir, prev + 1, next - prev);
+
+			if (mkdir(dir, mode) && errno != EEXIST) {
+				g_free(dir);
+				return -1;
+			}
+		}
+
+	g_free(dir);
+	return 0;
+}
+
 static void sim_op_error(struct ofono_modem *modem)
 {
 	struct sim_manager_data *sim = modem->sim_manager;
@@ -280,6 +313,11 @@ static void sim_op_error(struct ofono_modem *modem)
 		g_timeout_add(0, sim_op_next, modem);
 }
 
+#define SIM_CACHE_MODE 0600
+#define SIM_CACHE_PATH CONFIG_LOCALSTATEDIR "/lib/ofono/%s/%04x"
+#define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 2 + imsilen)
+#define SIM_CACHE_HEADER_SIZE 6
+
 static void sim_op_retrieve_cb(const struct ofono_error *error,
 				const unsigned char *data, int len, void *user)
 {
@@ -288,6 +326,10 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
 	struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
 	int total = op->length / op->record_length;
 
+	char *imsi = sim->imsi;
+	char *path;
+	int fd, ret;
+
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		if (op->current == 1)
 			sim_op_error(modem);
@@ -298,6 +340,23 @@ static void sim_op_retrieve_cb(const struct ofono_error *error,
 	op->cb(modem, 1, op->structure, op->length, op->current,
 		data, op->record_length, op->userdata);
 
+	if (op->cache && imsi) {
+		/* Cache the record */
+		path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
+		fd = open(path, O_WRONLY);
+		g_free(path);
+
+		if (fd == -1)
+			goto next;
+
+		if (lseek(fd, (op->current - 1) * op->record_length +
+					SIM_CACHE_HEADER_SIZE, SEEK_SET) !=
+				(off_t) -1)
+			write(fd, data, op->record_length);
+		close(fd);
+	}
+
+next:
 	if (op->current == total) {
 		op = g_queue_pop_head(sim->simop_q);
 
@@ -363,6 +422,11 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
 	struct sim_manager_data *sim = modem->sim_manager;
 	struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
 
+	char *imsi = sim->imsi;
+	char *path;
+	unsigned char fileinfo[6];
+	int fd = -1;
+
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		sim_op_error(modem);
 		return;
@@ -370,6 +434,12 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
 
 	op->structure = structure;
 	op->length = length;
+	/* Never cache card holder writable files */
+	op->cache = (
+			access[OFONO_SIM_FILE_CONDITION_UPDATE] ==
+			OFONO_SIM_FILE_ACCESS_ADM ||
+			access[OFONO_SIM_FILE_CONDITION_UPDATE] ==
+			OFONO_SIM_FILE_ACCESS_NEVER);
 
 	if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
 		op->record_length = length;
@@ -379,6 +449,30 @@ static void sim_op_info_cb(const struct ofono_error *error, int length,
 	op->current = 1;
 
 	g_timeout_add(0, sim_op_retrieve_next, modem);
+
+	if (op->cache && imsi) {
+		path = g_strdup_printf(SIM_CACHE_PATH, imsi, op->id);
+		if (create_dirs(path, SIM_CACHE_MODE | S_IXUSR) == 0)
+			fd = open(path, O_WRONLY | O_CREAT, SIM_CACHE_MODE);
+		g_free(path);
+
+		if (fd == -1) {
+			ofono_debug("Error %i creating cache file for "
+					"fileid %04x, IMSI %s",
+					errno, op->id, imsi);
+			return;
+		}
+
+		fileinfo[0] = error->type;
+		fileinfo[1] = length >> 8;
+		fileinfo[2] = length & 0xff;
+		fileinfo[3] = structure;
+		fileinfo[4] = record_length >> 8;
+		fileinfo[5] = record_length & 0xff;
+
+		write(fd, fileinfo, 6);
+		close(fd);
+	}
 }
 
 static gboolean sim_op_next(gpointer user_data)
@@ -397,6 +491,106 @@ static gboolean sim_op_next(gpointer user_data)
 	return FALSE;
 }
 
+struct sim_cache_callback {
+	ofono_sim_file_read_cb_t cb;
+	void *userdata;
+	struct ofono_modem *modem;
+	int error;
+	int fd;
+	enum ofono_sim_file_structure structure;
+	unsigned int record_length;
+	unsigned int total;
+};
+
+static gboolean sim_op_cached_callback(gpointer user)
+{
+	struct sim_cache_callback *cbs = user;
+	guint8 buffer[cbs->record_length];
+	unsigned int record;
+
+	if (cbs->error != OFONO_ERROR_TYPE_NO_ERROR) {
+		cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
+		goto cleanup;
+	}
+
+	for (record = 0; record < cbs->total; record++) {
+		if (read(cbs->fd, buffer, cbs->record_length) <
+				(int) cbs->record_length) {
+			cbs->cb(cbs->modem, 0, 0, 0, 0, 0, 0, 0);
+			break;
+		}
+
+		cbs->cb(cbs->modem, 1, cbs->structure,
+				cbs->record_length * cbs->total, record + 1,
+				buffer, cbs->record_length, cbs->userdata);
+	}
+
+cleanup:
+	close(cbs->fd);
+	g_free(cbs);
+
+	return FALSE;
+}
+
+static gboolean sim_op_check_cached(struct ofono_modem *modem, int fileid,
+			ofono_sim_file_read_cb_t cb, void *data)
+{
+	struct sim_manager_data *sim = modem->sim_manager;
+	char *imsi = sim->imsi;
+	char *path;
+	int fd;
+	unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
+	ssize_t len;
+	struct ofono_error error;
+	unsigned int file_length;
+	enum ofono_sim_file_structure structure;
+	unsigned int record_length;
+	struct sim_cache_callback *cbs;
+
+	if (!imsi)
+		return FALSE;
+
+	path = g_strdup_printf(SIM_CACHE_PATH, imsi, fileid);
+	fd = open(path, O_RDONLY);
+	g_free(path);
+
+	if (fd == -1) {
+		if (errno != ENOENT)
+			ofono_debug("Error %i opening cache file for "
+					"fileid %04x, IMSI %s",
+					errno, fileid, imsi);
+
+		return FALSE;
+	}
+
+	len = read(fd, fileinfo, SIM_CACHE_HEADER_SIZE);
+	if (len != SIM_CACHE_HEADER_SIZE)
+		return FALSE;
+
+	error.type = fileinfo[0];
+	file_length = (fileinfo[1] << 8) | fileinfo[2];
+	structure = fileinfo[3];
+	record_length = (fileinfo[4] << 8) | fileinfo[5];
+
+	if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
+		record_length = file_length;
+	if (record_length == 0 || file_length < record_length)
+		return FALSE;
+
+	cbs = g_new(struct sim_cache_callback, 1);
+	cbs->cb = cb;
+	cbs->userdata = data;
+	cbs->modem = modem;
+	cbs->error = error.type;
+	cbs->fd = fd;
+	cbs->structure = structure;
+	cbs->record_length = record_length;
+	cbs->total = file_length / record_length;
+	g_timeout_add(0, sim_op_cached_callback, cbs);
+
+	return TRUE;
+}
+
 int ofono_sim_read(struct ofono_modem *modem, int id,
 			ofono_sim_file_read_cb_t cb, void *data)
 {
@@ -409,6 +603,9 @@ int ofono_sim_read(struct ofono_modem *modem, int id,
 	if (modem->sim_manager == NULL)
 		return -1;
 
+	if (sim_op_check_cached(modem, id, cb, data))
+		return 0;
+
 	if (!sim->ops)
 		return -1;
 
-- 
1.6.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-20 16:12           ` Andrzej Zaborowski
  2009-07-20 14:24             ` Marcel Holtmann
@ 2009-07-27 23:13             ` Denis Kenzior
  2009-07-28  1:39               ` Andrzej Zaborowski
  1 sibling, 1 reply; 13+ messages in thread
From: Denis Kenzior @ 2009-07-27 23:13 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 445 bytes --]

> This uses plain files in /var/cache/ofono for storing contents of the
> operator lists to avoid possibly numerous queries to the SIM on every
> startup.  Files are indexed with IMSI.  I'm not 100% sure about the
> autoconf magic.
>
> Users need to rerun bootstrap-configure after applying this.
>
> Depends on [PATCH] Return SIM file access conditions from read_file_info.

This patch has been pushed.  Thanks.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-27 23:13             ` Denis Kenzior
@ 2009-07-28  1:39               ` Andrzej Zaborowski
  2009-07-29 22:24                 ` Denis Kenzior
  0 siblings, 1 reply; 13+ messages in thread
From: Andrzej Zaborowski @ 2009-07-28  1:39 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 493 bytes --]

2009/7/28 Denis Kenzior <denkenz@gmail.com>:
>> This uses plain files in /var/cache/ofono for storing contents of the
>> operator lists to avoid possibly numerous queries to the SIM on every
>> startup.  Files are indexed with IMSI.  I'm not 100% sure about the
>> autoconf magic.
>>
>> Users need to rerun bootstrap-configure after applying this.
>
> This patch has been pushed.

Thanks.  Attaching rebased patch to use Bluez autoconf snippet for
setting STORAGEDIR.

Regards

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Use-STORAGEDIR-autoconf-voodoo-from-Bluez.patch --]
[-- Type: text/x-patch, Size: 1892 bytes --]

From 857ab2447ce0f8dd3e69ab05f78ab6cb4dd3e7e7 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <balrog@zabor.org>
Date: Mon, 27 Jul 2009 18:36:11 +0200
Subject: [PATCH] Use STORAGEDIR autoconf voodoo from Bluez.

---
 configure.ac |   25 +++++++++++++++++++------
 src/sim.c    |    2 +-
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/configure.ac b/configure.ac
index f829b47..a5b4884 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,8 +8,6 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 AM_MAINTAINER_MODE
 
-AC_PREFIX_DEFAULT(/usr/local)
-
 AC_LANG_C
 
 AC_PROG_CC
@@ -96,10 +94,25 @@ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
 
 AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
 
-eval "eval LOCALSTATE_DIR=$localstatedir"
-AC_SUBST(LOCALSTATE_DIR)
-AC_DEFINE_UNQUOTED(CONFIG_LOCALSTATEDIR, "$LOCALSTATE_DIR",
-		   [Define to the location where state is stored.])
+AC_PREFIX_DEFAULT(/usr/local)
+
+if (test "${prefix}" = "NONE"); then
+	dnl no prefix and no localstatedir, so default to /var
+	if (test "$localstatedir" = '${prefix}/var'); then
+		AC_SUBST([localstatedir], ['/var'])
+	fi
+
+	prefix="${ac_default_prefix}"
+fi
+
+if (test "$localstatedir" = '${prefix}/var'); then
+	storagedir="${prefix}/var/lib/ofono"
+else
+	storagedir="${localstatedir}/lib/ofono"
+fi
+
+AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
+			[Directory for the storage files])
 
 COMPILER_FLAGS
 
diff --git a/src/sim.c b/src/sim.c
index ff4e63d..5122ee5 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -315,7 +315,7 @@ static void sim_op_error(struct ofono_modem *modem)
 }
 
 #define SIM_CACHE_MODE 0600
-#define SIM_CACHE_PATH CONFIG_LOCALSTATEDIR "/lib/ofono/%s/%04x"
+#define SIM_CACHE_PATH STORAGEDIR "/%s/%04x"
 #define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 2 + imsilen)
 #define SIM_CACHE_HEADER_SIZE 6
 
-- 
1.6.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Cache EF-PNN, EF-OPL sim files on disk.
  2009-07-28  1:39               ` Andrzej Zaborowski
@ 2009-07-29 22:24                 ` Denis Kenzior
  0 siblings, 0 replies; 13+ messages in thread
From: Denis Kenzior @ 2009-07-29 22:24 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 146 bytes --]

> Thanks.  Attaching rebased patch to use Bluez autoconf snippet for
> setting STORAGEDIR.

Path has been pushed. Thanks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2009-07-29 22:24 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-07-05  3:14 [PATCH] Cache EF-PNN, EF-OPL sim files on disk Andrzej Zaborowski
2009-07-14 20:54 ` Denis Kenzior
2009-07-18  4:34   ` Andrzej Zaborowski
2009-07-18  6:02     ` Denis Kenzior
2009-07-18 11:32       ` andrzej zaborowski
2009-07-18 11:39         ` Marcel Holtmann
2009-07-18 12:42         ` andrzej zaborowski
2009-07-20 16:12           ` Andrzej Zaborowski
2009-07-20 14:24             ` Marcel Holtmann
2009-07-20 14:56               ` andrzej zaborowski
2009-07-27 23:13             ` Denis Kenzior
2009-07-28  1:39               ` Andrzej Zaborowski
2009-07-29 22:24                 ` Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.