persistent property store - first try

Joe Shaw joeshaw at novell.com
Thu Jun 10 10:13:22 PDT 2004


Hey,

On Thu, 2004-06-10 at 14:14 +0200, Kay Sievers wrote:
> On Wed, Jun 09, 2004 at 03:57:58AM +0200, Kay Sievers wrote:
> 
> Here is a new version which creates, saves and reads the uuid for store.
> The uuid is handled by calling hal_pstore_init("/var/lib/hal") as the
> very first action. So every call to hal_pstore_open(<path>) will create
> a new store with the uuid as the last directory (e.g. for storing per box
> data in the users $HOME).

This is great!  I'm amazed by how relatively little code this is.  Very
straightforward, which I like. :)

> The save action is still only triggered (by the dirty hack) if the
> property name begins with "save*". You can watch it by doing:
> 
>   hal-set-property --udi /org/freedesktop/Hal/devices/usb_46d_c00e_1100_-1_noserial --string yeah2 --key save.key
> 
> Hmm, we may add a "persistent" attribute to "struct _HalProperty" and a
> look at this attribute if hal_device_property_set_<type>() is called?
> So we can set this attribute if persistence is wanted.

Yeah, I think we probably will need to do this.  Maybe we should add a
hal_device_property_set_persistent() (and an equivalent function on
HalProperty) and make it the responsibility of the things setting it?
Alternative is adding _persistent variants on all the set functions, and
that duplicates a bunch of API.  Bleh.

Comments on the patch:

> --- /dev/null	2004-02-23 22:02:56.000000000 +0100
> +++ hal.kay/hal/hald/pstore.c	2004-06-10 13:23:35.585537392 +0200
> @@ -0,0 +1,369 @@
> +/***************************************************************************
> + * CVSID: $Id: $
> + *
> + * pstore.c : persistent property store on disk
> + *
> + * Copyright (C) 2004 Kay Sievers, <kay.sievers at vrfy.org>
> + *
> + * Licensed under the Academic Free License version 2.0
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + **************************************************************************/
> +
> +#ifdef HAVE_CONFIG_H
> +#  include <config.h>
> +#endif
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <time.h>
> +#include <sys/stat.h>
> +#include <glib.h>
> +
> +#include "logger.h"
> +#include "pstore.h"
> +
> +#define PSTR		"String:"
> +#define PINT32		"Int32:"
> +#define PBOOL		"Bool:"
> +#define PDOUBLE		"Double:"
> +#define UDI_STRIP	"/org/freedesktop/Hal"
> +
> +static char *pstore_uuid = NULL;
> +
> +struct _HalPStore {
> +	char *path;
> +	char *uuid;
> +};
> +
> +/** Create relative or absolute path.
> + *
> + */
> +static int
> +create_path(const char *path)
> +{
> +	char *p;
> +	char *pos;
> +	struct stat stats;
> +
> +	if (stat (path, &stats) == 0)
> +		return 0;
> +
> +	p = g_strdup (path);
> +	pos = strrchr (p, '/');
> +	if (pos != NULL && pos != p) {
> +		pos[0] = '\0';
> +		create_path (p);
> +	}
> +	g_free (p);
> +
> +	return mkdir(path, 0755);
> +}
> +
> +/** Return absolute filename with simplified udi string.
> + *
> + */
> +static char
> +*build_path (HalPStore *pstore,
> +	     const char *udi, const char *file)
> +{
> +	char *path;
> +	const char *udi_s;
> +
> +	udi_s = udi;
> +	/* strip namespace part of udi string */
> +	if (udi != NULL &&
> +	    g_ascii_strncasecmp(udi, UDI_STRIP, sizeof(UDI_STRIP)-1) == 0)
> +			udi_s = &udi[sizeof(UDI_STRIP)-1];
> +
> +	path = g_build_filename (G_DIR_SEPARATOR_S,
> +				 pstore->path, pstore->uuid,
> +				 udi_s, file, NULL);
> +
> +	return path;
> +}
> +
> +/** Return whole file content.
> + *
> + */
> +static char
> +*read_file (const char *file)
> +{
> +	GIOChannel *io;
> +	GError *error = NULL;
> +	char *buf;
> +	int buf_len;
> +
> +	HAL_DEBUG (("reading %s", file));
> +	io = g_io_channel_new_file (file, "r", &error);
> +	if (error) {
> +		HAL_INFO (("couldn't open %s", file));
> +		return NULL;
> +	}
> +
> +	g_io_channel_read_to_end (io, &buf, &buf_len, &error);
> +	g_io_channel_shutdown (io, TRUE, &error);
> +	if (error)
> +		return NULL;
> +
> +	return buf;
> +}

You can probably just do away with this whole function and use
g_file_get_contents() instead.

> +
> +/** Init pstore system by reading or generating our uuid.
> + *
> + */
> +void
> +hal_pstore_init (const char *path)
> +{
> +	char *file;
> +	char *uuid;
> +	char hostname[HOST_NAME_MAX];
> +	char *p;
> +	GIOChannel *io;
> +	GError *error = NULL;
> +	int written;
> +
> +	file = g_build_filename (G_DIR_SEPARATOR_S,
> +				 path, "uuid", NULL);
> +	uuid = read_file(file);
> +
> +	if (uuid == NULL) {
> +		/* create new uuid and save it to disk */
> +		gethostname (hostname, HOST_NAME_MAX);
> +		uuid = g_strdup_printf ("%s-%lx", hostname, time(NULL));
> +
> +		p = g_build_path (G_DIR_SEPARATOR_S,
> +				  path, NULL);
> +		create_path(p);
> +		g_free(p);
> +
> +		io = g_io_channel_new_file (file, "w", &error);
> +		g_io_channel_write_chars (io, uuid, -1, &written, &error);
> +		g_io_channel_shutdown (io, TRUE, &error);
> +	}

If you don't care about the errors returned from g_io_channel_new_file
(), write_chars(), etc. it's always safe to pass NULL in as the GError
argument.  Otherwise you'll need to handle those or else you get failed
assertions because error won't be NULL at some later point.

You'd probably want to do something like:

        io = g_io_channel_new_file (file, "w", &error);
        
        if (error != NULL) {
        	HAL_WARN (("Error creating file: %s", error->msg));
                goto cleanup;
        }
        
        g_io_channel_write_chars (io, uuid, -1, &written, &error);
        
        if (error != NULL) { ... }
        
        [later...]
        
        HAL_DEBUG(("uuid is %s", uuid));
        pstore_uuid = uuid;
        
        cleanup:
        if (error != NULL)
        	g_error_free (error);
        
        g_free (file);

> +
> +	HAL_DEBUG (("uuid is %s", uuid));
> +	pstore_uuid = uuid;
> +
> +	g_free (file);
> +}
> +
> +
> +/** Open pstore on the given location.
> + *
> + */
> +HalPStore
> +*hal_pstore_open (const char *path)
> +{
> +	HalPStore *pstore;
> +	char *p;
> +
> +	pstore = g_new0 (HalPStore, 1);
> +
> +	pstore->path = g_strdup (path);
> +
> +	pstore->uuid = pstore_uuid;
> +
> +	p = g_build_path (G_DIR_SEPARATOR_S,
> +			  pstore->path, pstore->uuid, NULL);
> +	create_path(p);
> +	g_free(p);
> +
> +	HAL_DEBUG (("opened pstore at %s/%s/", pstore->path, pstore->uuid));
> +
> +	return pstore;
> +}
> +
> +/** Turn around the OPEN sign, in the store's entrance :)
> + *
> + */
> +void
> +hal_pstore_close (HalPStore *pstore)
> +{
> +	g_free (pstore->path);
> +	g_free (pstore->uuid);
> +	g_free (pstore);
> +}
> +
> +/** Save a property value to the disk.
> + *
> + */
> +void
> +hal_pstore_save_property (HalPStore *pstore,
> +			  HalDevice *device, HalProperty *prop)
> +{
> +	char *path;
> +	char *file;
> +	GIOChannel *io;
> +	GError *error = NULL;
> +	char *str;
> +	int written;
> +
> +	path = build_path (pstore, hal_device_get_udi (device), NULL);
> +	create_path(path);
> +	g_free(path);
> +
> +	file = build_path (pstore,
> +			   hal_device_get_udi (device),
> +			   hal_property_get_key (prop));
> +
> +	io = g_io_channel_new_file (file, "w", &error);
> +	if (error) {
> +		HAL_INFO (("couldn't open %s for writing", file));
> +		g_free (file);
> +		return;
> +	}

You'll need to call g_error_free (error) in this block or you'll leak
the GError.  Also you should probably include the error message in the
HAL_INFO.  I think it's error->msg.

> +
> +	HAL_INFO (("write %s to disk", hal_property_get_key (prop)));
> +
> +	switch (hal_property_get_type (prop)) {
> +	case DBUS_TYPE_STRING:
> +		g_io_channel_write_chars (io, PSTR, -1, &written, &error);
> +		break;
> +	case DBUS_TYPE_INT32:
> +		g_io_channel_write_chars (io, PINT32, -1, &written, &error);
> +		break;
> +	case DBUS_TYPE_BOOLEAN:
> +		g_io_channel_write_chars (io, PBOOL, -1, &written, &error);
> +		break;
> +	case DBUS_TYPE_DOUBLE:
> +		g_io_channel_write_chars (io, PDOUBLE, -1, &written, &error);
> +		break;
> +	default:
> +		HAL_INFO (("unknown property type %s",
> +			   hal_property_get_key (prop)));
> +	}
> +
> +	str = hal_property_to_string (prop);
> +	g_io_channel_write_chars (io, str, -1, &written, &error);
> +
> +	g_free (file);
> +	g_free (str);
> +	g_io_channel_shutdown (io, TRUE, &error);
> +}

Same stuff wrt GErrors here.

> +/** Load a stored property value from the disk.
> + *
> + */
> +void
> +hal_pstore_load_property (HalPStore *pstore,
> +			   HalDevice *device, const char *key)
> +{
> +	char *file;
> +	char *buf;
> +	char *str;
> +	int i;
> +	double d;
> +
> +	file = build_path (pstore, hal_device_get_udi (device), key);
> +
> +	buf = read_file (file);
> +	g_free (file);
> +	if (buf == NULL)
> +		return;
> +
> +	if (g_ascii_strncasecmp (buf, PSTR, sizeof (PSTR)-1) == 0) {
> +		str =  &buf[sizeof (PSTR)-1];
> +		hal_device_property_set_string (device, key, str);
> +		HAL_INFO (("STRING %s read for %s", str, key));
> +		goto exit;
> +	}
> +
> +	if (g_ascii_strncasecmp (buf, PINT32, sizeof (PINT32)-1) == 0) {
> +		str =  &buf[sizeof (PINT32)-1];
> +		i = strtol (str, NULL, 10);
> +		hal_device_property_set_int (device, key, i);
> +		goto exit;
> +	}
> +
> +	if (g_ascii_strncasecmp (buf, PBOOL, sizeof (PBOOL)-1) == 0) {
> +		str =  &buf[sizeof (PBOOL)-1];
> +		if (g_ascii_strcasecmp (str, "true") == 0)
> +			i = TRUE;
> +		else
> +			i = FALSE;
> +		hal_device_property_set_bool (device, key, i);
> +		goto exit;
> +	}
> +
> +	if (g_ascii_strncasecmp (buf, PDOUBLE, sizeof (PDOUBLE)-1) == 0) {
> +		str =  &buf[sizeof (PDOUBLE)-1];
> +		d = atof (str);
> +		hal_device_property_set_double (device, key, d);
> +		goto exit;
> +	}
> +
> +	HAL_INFO (("error reading pstore property %s", key));
> +
> +exit:
> +	g_free (buf);
> +}
> +
> +/** Delete a stored property from the disk.
> + *
> + */
> +void
> +hal_pstore_delete_property (HalPStore *pstore,
> +			    HalDevice *device, HalProperty *prop)
> +{
> +	char *file;
> +
> +	file = build_path (pstore,
> +			   hal_device_get_udi (device),
> +			   hal_property_get_key (prop));
> +
> +	HAL_DEBUG (("unlinking %s", file));
> +
> +	unlink(file);
> +	g_free(file);
> +}
> +
> +/** Load all stored properties of a device from the disk.
> + *
> + */
> +void
> +hal_pstore_load_device (HalPStore *pstore,
> +			HalDevice *device)
> +{
> +	GDir *dir;
> +	GError *error  = NULL;
> +	const char *dirname;
> +	char *path;
> +
> +	path = build_path (pstore,
> +			   hal_device_get_udi (device),
> +			   NULL);
> +
> +	HAL_DEBUG (("reading directory %s", path));
> +
> +	dir = g_dir_open (path, 0, &error);
> +	if (error)
> +		goto exit;
> +
> +	while (1) {
> +		dirname = g_dir_read_name (dir);
> +		if (dirname == NULL)
> +			break;
> +
> +		hal_pstore_load_property (pstore, device, dirname);
> +	}
> +
> +	g_dir_close (dir);
> +
> +exit:
> +	g_free (path);
> +}
> _______________________________________________
> hal mailing list
> hal at freedesktop.org
> http://freedesktop.org/mailman/listinfo/hal


_______________________________________________
hal mailing list
hal at freedesktop.org
http://freedesktop.org/mailman/listinfo/hal



More information about the Hal mailing list