From 9852f953a647d3e32a0637153d00096d4ac6942a Mon Sep 17 00:00:00 2001 From: Andrzej Zaborowski 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 #include #include +#include +#include +#include +#include #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