libsigrok  0.5.2
sigrok hardware access and backend library
session_file.c
Go to the documentation of this file.
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <zip.h>
24 #include <errno.h>
25 #include <glib.h>
26 #include <glib/gstdio.h>
27 #include <libsigrok/libsigrok.h>
28 #include "libsigrok-internal.h"
29 
30 /** @cond PRIVATE */
31 #define LOG_PREFIX "session-file"
32 /** @endcond */
33 
34 /**
35  * @file
36  *
37  * Loading and saving libsigrok session files.
38  */
39 
40 /**
41  * @addtogroup grp_session
42  *
43  * @{
44  */
45 
47 static int session_driver_initialized = 0;
48 
49 #if !HAVE_ZIP_DISCARD
50 /* Replacement for zip_discard() if it isn't available. */
51 /** @private */
52 SR_PRIV void sr_zip_discard(struct zip *archive)
53 {
54  if (zip_unchange_all(archive) < 0 || zip_close(archive) < 0)
55  sr_err("Failed to discard ZIP archive: %s", zip_strerror(archive));
56 }
57 #endif
58 
59 /**
60  * Read metadata entries from a session archive.
61  *
62  * @param[in] archive An open ZIP archive.
63  * @param[in] entry Stat buffer filled in for the metadata archive member.
64  *
65  * @return A new key/value store containing the session metadata.
66  *
67  * @private
68  */
69 SR_PRIV GKeyFile *sr_sessionfile_read_metadata(struct zip *archive,
70  const struct zip_stat *entry)
71 {
72  GKeyFile *keyfile;
73  GError *error;
74  struct zip_file *zf;
75  char *metabuf;
76  int metalen;
77 
78  if (entry->size > G_MAXINT || !(metabuf = g_try_malloc(entry->size))) {
79  sr_err("Metadata buffer allocation failed.");
80  return NULL;
81  }
82  zf = zip_fopen_index(archive, entry->index, 0);
83  if (!zf) {
84  sr_err("Failed to open metadata: %s", zip_strerror(archive));
85  g_free(metabuf);
86  return NULL;
87  }
88  metalen = zip_fread(zf, metabuf, entry->size);
89  if (metalen < 0) {
90  sr_err("Failed to read metadata: %s", zip_file_strerror(zf));
91  zip_fclose(zf);
92  g_free(metabuf);
93  return NULL;
94  }
95  zip_fclose(zf);
96 
97  keyfile = g_key_file_new();
98  error = NULL;
99  g_key_file_load_from_data(keyfile, metabuf, metalen,
100  G_KEY_FILE_NONE, &error);
101  g_free(metabuf);
102 
103  if (error) {
104  sr_err("Failed to parse metadata: %s", error->message);
105  g_error_free(error);
106  g_key_file_free(keyfile);
107  return NULL;
108  }
109  return keyfile;
110 }
111 
112 /** @private */
113 SR_PRIV int sr_sessionfile_check(const char *filename)
114 {
115  struct zip *archive;
116  struct zip_file *zf;
117  struct zip_stat zs;
118  uint64_t version;
119  int ret;
120  char s[11];
121 
122  if (!filename)
123  return SR_ERR_ARG;
124 
125  if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
126  sr_err("Not a regular file: %s.", filename);
127  return SR_ERR;
128  }
129 
130  if (!(archive = zip_open(filename, 0, NULL)))
131  /* No logging: this can be used just to check if it's
132  * a sigrok session file or not. */
133  return SR_ERR;
134 
135  /* check "version" */
136  if (!(zf = zip_fopen(archive, "version", 0))) {
137  sr_dbg("Not a sigrok session file: no version found.");
138  zip_discard(archive);
139  return SR_ERR;
140  }
141  ret = zip_fread(zf, s, sizeof(s) - 1);
142  if (ret < 0) {
143  sr_err("Failed to read version file: %s",
144  zip_file_strerror(zf));
145  zip_fclose(zf);
146  zip_discard(archive);
147  return SR_ERR;
148  }
149  zip_fclose(zf);
150  s[ret] = '\0';
151  version = g_ascii_strtoull(s, NULL, 10);
152  if (version == 0 || version > 2) {
153  sr_dbg("Cannot handle sigrok session file version %" PRIu64 ".",
154  version);
155  zip_discard(archive);
156  return SR_ERR;
157  }
158  sr_spew("Detected sigrok session file version %" PRIu64 ".", version);
159 
160  /* read "metadata" */
161  if (zip_stat(archive, "metadata", 0, &zs) < 0) {
162  sr_dbg("Not a valid sigrok session file.");
163  zip_discard(archive);
164  return SR_ERR;
165  }
166  zip_discard(archive);
167 
168  return SR_OK;
169 }
170 
171 /** @private */
172 SR_PRIV struct sr_dev_inst *sr_session_prepare_sdi(const char *filename, struct sr_session **session)
173 {
174  struct sr_dev_inst *sdi = NULL;
175 
176  sdi = g_malloc0(sizeof(struct sr_dev_inst));
177  sdi->driver = &session_driver;
178  sdi->status = SR_ST_INACTIVE;
179  if (!session_driver_initialized) {
180  /* first device, init the driver */
181  session_driver_initialized = 1;
182  sdi->driver->init(sdi->driver, NULL);
183  }
184  sr_dev_open(sdi);
185  sr_session_dev_add(*session, sdi);
186  (*session)->owned_devs = g_slist_append((*session)->owned_devs, sdi);
188  g_variant_new_string(filename));
189 
190  return sdi;
191 }
192 
193 /**
194  * Load the session from the specified filename.
195  *
196  * @param ctx The context in which to load the session.
197  * @param filename The name of the session file to load.
198  * @param session The session to load the file into.
199  *
200  * @retval SR_OK Success
201  * @retval SR_ERR_MALLOC Memory allocation error
202  * @retval SR_ERR_DATA Malformed session file
203  * @retval SR_ERR This is not a session file
204  */
205 SR_API int sr_session_load(struct sr_context *ctx, const char *filename,
206  struct sr_session **session)
207 {
208  GKeyFile *kf;
209  GError *error;
210  struct zip *archive;
211  struct zip_stat zs;
212  struct sr_dev_inst *sdi;
213  struct sr_channel *ch;
214  int ret, i, j;
215  uint64_t tmp_u64;
216  int total_channels, total_analog, k;
217  GSList *l;
218  int unitsize;
219  char **sections, **keys, *val;
220  char channelname[SR_MAX_CHANNELNAME_LEN + 1];
221  gboolean file_has_logic;
222 
223  if ((ret = sr_sessionfile_check(filename)) != SR_OK)
224  return ret;
225 
226  if (!(archive = zip_open(filename, 0, NULL)))
227  return SR_ERR;
228 
229  if (zip_stat(archive, "metadata", 0, &zs) < 0) {
230  zip_discard(archive);
231  return SR_ERR;
232  }
233  kf = sr_sessionfile_read_metadata(archive, &zs);
234  zip_discard(archive);
235  if (!kf)
236  return SR_ERR_DATA;
237 
238  if ((ret = sr_session_new(ctx, session)) != SR_OK) {
239  g_key_file_free(kf);
240  return ret;
241  }
242 
243  total_channels = 0;
244 
245  error = NULL;
246  ret = SR_OK;
247  file_has_logic = FALSE;
248  sections = g_key_file_get_groups(kf, NULL);
249  for (i = 0; sections[i] && ret == SR_OK; i++) {
250  if (!strcmp(sections[i], "global"))
251  /* nothing really interesting in here yet */
252  continue;
253  if (!strncmp(sections[i], "device ", 7)) {
254  /* device section */
255  sdi = NULL;
256  keys = g_key_file_get_keys(kf, sections[i], NULL, NULL);
257 
258  /* File contains analog data if there are analog channels. */
259  total_analog = g_key_file_get_integer(kf, sections[i],
260  "total analog", &error);
261  if (total_analog > 0 && !error)
262  sdi = sr_session_prepare_sdi(filename, session);
263  g_clear_error(&error);
264 
265  /* File contains logic data if a capturefile is set. */
266  val = g_key_file_get_string(kf, sections[i],
267  "capturefile", &error);
268  if (val && !error) {
269  if (!sdi)
270  sdi = sr_session_prepare_sdi(filename, session);
272  g_variant_new_string(val));
273  g_free(val);
274  file_has_logic = TRUE;
275  }
276  g_clear_error(&error);
277 
278  for (j = 0; keys[j]; j++) {
279  if (!strcmp(keys[j], "samplerate")) {
280  val = g_key_file_get_string(kf, sections[i],
281  keys[j], &error);
282  if (!sdi || !val || sr_parse_sizestring(val,
283  &tmp_u64) != SR_OK) {
284  g_free(val);
285  ret = SR_ERR_DATA;
286  break;
287  }
288  g_free(val);
290  g_variant_new_uint64(tmp_u64));
291  } else if (!strcmp(keys[j], "unitsize") && file_has_logic) {
292  unitsize = g_key_file_get_integer(kf, sections[i],
293  keys[j], &error);
294  if (!sdi || unitsize <= 0 || error) {
295  ret = SR_ERR_DATA;
296  break;
297  }
299  g_variant_new_uint64(unitsize));
300  } else if (!strcmp(keys[j], "total probes")) {
301  total_channels = g_key_file_get_integer(kf,
302  sections[i], keys[j], &error);
303  if (!sdi || total_channels < 0 || error) {
304  ret = SR_ERR_DATA;
305  break;
306  }
308  g_variant_new_int32(total_channels));
309  for (k = 0; k < total_channels; k++) {
310  g_snprintf(channelname, sizeof(channelname),
311  "%d", k);
312  sr_channel_new(sdi, k, SR_CHANNEL_LOGIC,
313  FALSE, channelname);
314  }
315  } else if (!strcmp(keys[j], "total analog")) {
316  total_analog = g_key_file_get_integer(kf,
317  sections[i], keys[j], &error);
318  if (!sdi || total_analog < 0 || error) {
319  ret = SR_ERR_DATA;
320  break;
321  }
323  g_variant_new_int32(total_analog));
324  for (k = total_channels; k < (total_channels + total_analog); k++) {
325  g_snprintf(channelname, sizeof(channelname),
326  "%d", k);
327  sr_channel_new(sdi, k, SR_CHANNEL_ANALOG,
328  FALSE, channelname);
329  }
330  } else if (!strncmp(keys[j], "probe", 5)) {
331  tmp_u64 = g_ascii_strtoull(keys[j] + 5, NULL, 10);
332  if (!sdi || tmp_u64 == 0 || tmp_u64 > G_MAXINT) {
333  ret = SR_ERR_DATA;
334  break;
335  }
336  ch = g_slist_nth_data(sdi->channels, tmp_u64 - 1);
337  if (!ch) {
338  ret = SR_ERR_DATA;
339  break;
340  }
341  val = g_key_file_get_string(kf, sections[i],
342  keys[j], &error);
343  if (!val) {
344  ret = SR_ERR_DATA;
345  break;
346  }
347  /* sr_session_save() */
348  sr_dev_channel_name_set(ch, val);
349  g_free(val);
350  sr_dev_channel_enable(ch, TRUE);
351  } else if (!strncmp(keys[j], "analog", 6)) {
352  tmp_u64 = g_ascii_strtoull(keys[j]+6, NULL, 10);
353  if (!sdi || tmp_u64 == 0 || tmp_u64 > G_MAXINT) {
354  ret = SR_ERR_DATA;
355  break;
356  }
357  ch = NULL;
358  for (l = sdi->channels; l; l = l->next) {
359  ch = l->data;
360  if ((guint64)ch->index == tmp_u64 - 1)
361  break;
362  else
363  ch = NULL;
364  }
365  if (!ch) {
366  ret = SR_ERR_DATA;
367  break;
368  }
369  val = g_key_file_get_string(kf, sections[i],
370  keys[j], &error);
371  if (!val) {
372  ret = SR_ERR_DATA;
373  break;
374  }
375  /* sr_session_save() */
376  sr_dev_channel_name_set(ch, val);
377  g_free(val);
378  sr_dev_channel_enable(ch, TRUE);
379  }
380  }
381  g_strfreev(keys);
382  }
383  }
384  g_strfreev(sections);
385  g_key_file_free(kf);
386 
387  if (error) {
388  sr_err("Failed to parse metadata: %s", error->message);
389  g_error_free(error);
390  }
391  return ret;
392 }
393 
394 /** @} */
Generic/unspecified error.
Definition: libsigrok.h:68
The device supports setting the number of logic channels.
Definition: libsigrok.h:849
The device supports setting the number of analog channels.
Definition: libsigrok.h:852
int sr_session_load(struct sr_context *ctx, const char *filename, struct sr_session **session)
Load the session from the specified filename.
Definition: session_file.c:205
int sr_session_new(struct sr_context *ctx, struct sr_session **new_session)
Create a new session.
Definition: session.c:214
Channel type is logic channel.
Definition: libsigrok.h:590
No error.
Definition: libsigrok.h:67
int sr_dev_channel_name_set(struct sr_channel *channel, const char *name)
Set the name of the specified channel.
Definition: device.c:116
The public libsigrok header file to be used by frontends.
The device supports specifying a capturefile to inject.
Definition: libsigrok.h:1032
The device supports specifying the capturefile unit size.
Definition: libsigrok.h:1035
Channel type is analog channel.
Definition: libsigrok.h:592
Data is invalid.
Definition: libsigrok.h:77
int sr_parse_sizestring(const char *sizestring, uint64_t *size)
Convert a "natural" string representation of a size value to uint64_t.
Definition: strutil.c:883
SR_PRIV struct sr_dev_driver session_driver
int sr_dev_channel_enable(struct sr_channel *channel, gboolean state)
Enable or disable a channel.
Definition: device.c:140
int sr_config_set(const struct sr_dev_inst *sdi, const struct sr_channel_group *cg, uint32_t key, GVariant *data)
Set value of a configuration key in a device instance.
Definition: hwdriver.c:838
The device supports setting its samplerate, in Hz.
Definition: libsigrok.h:759
int sr_dev_open(struct sr_dev_inst *sdi)
Open the specified device instance.
Definition: device.c:664
#define SR_PRIV
Definition: libsigrok.h:128
Device driver data.
Definition: libsigrok.h:1138
#define SR_MAX_CHANNELNAME_LEN
Definition: libsigrok.h:83
Function argument error.
Definition: libsigrok.h:70
int index
The index of this channel, starting at 0.
Definition: libsigrok.h:601
int sr_session_dev_add(struct sr_session *session, struct sr_dev_inst *sdi)
Add a device instance to a session.
Definition: session.c:317
The device instance is live, but not in use.
Definition: libsigrok.h:1130
Information on single channel.
Definition: libsigrok.h:596
Opaque structure representing a libsigrok session.
Definition: libsigrok.h:456
Session filename.
Definition: libsigrok.h:1029
Opaque structure representing a libsigrok context.
#define SR_API
Definition: libsigrok.h:121