/etc/sysconfig/udpgate configuration file read/write support.
authorshort <>
Sun, 23 May 2004 11:03:19 +0000 (11:03 +0000)
committershort <>
Sun, 23 May 2004 11:03:19 +0000 (11:03 +0000)
src/Makefile.am
src/configuration.c [new file with mode: 0644]
src/configuration.h [new file with mode: 0644]
src/main.c
src/network.c
src/network.h
src/ui-gnome.c

index 2ca3bad..afc9da3 100644 (file)
@@ -37,6 +37,8 @@ GNOME_SRCS= \
 endif
 
 udpgate_SOURCES= \
+               configuration.c \
+               configuration.h \
                main.c \
                main.h \
                network.c \
diff --git a/src/configuration.c b/src/configuration.c
new file mode 100644 (file)
index 0000000..a06feab
--- /dev/null
@@ -0,0 +1,291 @@
+/* $Id$
+ * UDP Gateway persistent configuration
+ * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
+ * 
+ * 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; exactly version 2 of June 1991 is required
+ * 
+ * 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
+ */
+
+
+#include "config.h"
+
+#include <glib/gmessages.h>
+#include <glib/ghash.h>
+#include <glib/gstring.h>
+#include <glib/gmem.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <glib/gstrfuncs.h>
+#include <string.h>
+
+#include "configuration.h"     /* self */
+#include "network.h"
+#include "main.h"
+
+
+/* OK, GConf would be nice.
+ * Unfortunately the fully-static build does not support GConf process spawn at
+ * all. Also /etc/sysconfig directory is standard for the daemon services.
+ */
+
+
+/* Config: */
+#define CONFIGURATION_FILE "/etc/sysconfig/udpgate"
+
+
+static GHashTable *configuration_hash_new(void)
+{
+       return g_hash_table_new_full(g_str_hash,g_str_equal,
+                       (GDestroyNotify)g_free, /* key_equal_func; of g_strdup() strings */
+                       (GDestroyNotify)g_free);        /* value_equal_func; of g_strdup() strings */
+}
+
+static GHashTable *configuration_comments_hash_new(void)
+{
+static GHashTable *hash;
+
+       if (!hash) {
+               hash=g_hash_table_new(g_str_hash,g_str_equal);
+               g_hash_table_insert(hash,"PORT",_("Local UDP port"));
+               }
+       return hash;
+}
+
+static gboolean configuration_file_write(const gchar *file_content)
+{
+FILE *f;
+
+       g_return_val_if_fail(file_content!=NULL,FALSE);
+
+       if (!(f=fopen(CONFIGURATION_FILE,("w")))) {
+               g_warning(_("Error write opening configuration file \"%s\": %m"),CONFIGURATION_FILE);
+               return FALSE;
+               }
+       if (fputs(file_content,f)<0) {
+               g_warning(_("Error writing configuration file \"%s\": %m"),CONFIGURATION_FILE);
+               fclose(f);      /* errors ignored */
+               return FALSE;
+               }
+       if (fclose(f))
+               g_warning(_("Error closing configuration file \"%s\": %m"),CONFIGURATION_FILE);
+       return TRUE;
+}
+
+struct configuration_hash_readwrite_hash_flush_foreach_param {
+       GString *gstring;
+       GHashTable *hash_flushed;
+       gboolean modified;
+       };
+static void configuration_hash_readwrite_hash_flush_foreach
+               (const gchar *key,const gchar *value,struct configuration_hash_readwrite_hash_flush_foreach_param *param)
+{
+const gchar *comment;
+
+       g_return_if_fail(key!=NULL);
+       g_return_if_fail(value!=NULL);
+       g_return_if_fail(param!=NULL);
+       g_return_if_fail(param->gstring!=NULL);
+       g_return_if_fail(param->hash_flushed!=NULL);
+
+       /* Already written out? */
+       if (g_hash_table_lookup(param->hash_flushed,key))
+               return;
+       if (!(comment=g_hash_table_lookup(configuration_comments_hash_new(),key)))
+               comment=_("*** UNDOCUMENTED ***");
+       g_string_append(param->gstring,udpgate_printf_alloca("# %s: %s\n%s=%s\n",key,comment,key,value));
+       param->modified=TRUE;
+       if (optarg_verbose)
+               g_message(_("Appended configuration variable name \"%s\", value \"%s\""),key,value);
+}
+
+/* hash_flush==NULL => read info and return new (GHashTable *)
+ * hash_flush!=NULL => update configuration file by this (GHashTable *)
+ */
+/* FIXME: File locking! */
+static GHashTable *configuration_hash_readwrite(GHashTable *hash_flush)
+{
+FILE *f;
+char line[LINE_MAX];
+int lineno;
+GHashTable *hash_fill;
+/* Which 'keys' of 'hash_flush' were already written. 'key' must be g_free()able.
+ * Any 'value' is ignored. 'value' must be !=NULL. 'value' must be g_free()able.
+ */
+GHashTable *hash_flushed;
+GString *gstring;
+gboolean modified=FALSE;       /* 'gstring' contains modified value */
+gboolean already_written=FALSE;
+
+open_retry:
+       if (!(f=fopen(CONFIGURATION_FILE,(!hash_flush ? "r" : "rw")))) {
+               if (errno!=ENOENT)
+                       g_warning(_("Error r/o opening configuration file \"%s\": %m"),CONFIGURATION_FILE);
+               if (!hash_flush || already_written)
+                       return NULL;
+               else {
+                       already_written=TRUE;
+                       if (!configuration_file_write(_(
+"# Configuration file for UDPGate - see its man page udpgate(1).\n"
+"\n"
+                                       )))
+                               return NULL;
+                       goto open_retry;
+                       }
+               }
+       if (!hash_flush) {
+               hash_fill=configuration_hash_new();
+               hash_flushed=NULL;
+               gstring=NULL;
+               }
+       else {
+               hash_fill=NULL;
+               hash_flushed=configuration_hash_new();
+               gstring=g_string_new(NULL);
+               }
+       lineno=0;
+       while (errno=0,fgets(line,sizeof(line),f)) {
+char *s;
+char *varname_start,*varname_stop,varname_stop_orig;
+char *varcontent_start,*varcontent_stop,varcontent_stop_orig;
+const gchar *value;
+
+               lineno++;
+               s=line;
+               varname_stop=NULL;
+               varcontent_stop=NULL;
+               /* Parse: ^\s*([[:alnum:]])\s*=\s*(\S*)\s*$
+                */
+               while (*s && isspace(*s)) s++;
+               if (!*s || *s=='#') {
+err_append:
+                       if (gstring)
+                               g_string_append(gstring,line);
+                       continue;
+                       }
+               varname_start=s;
+               while (*s && isalnum(*s)) s++;
+               varname_stop=s;
+               varname_stop_orig=*varname_stop;
+               while (*s && isspace(*s)) s++;
+               if (*s++!='=') {
+err_line:
+                       if (varname_stop)
+                               *varname_stop=varname_stop_orig;
+                       if (varcontent_stop)
+                               *varcontent_stop=varcontent_stop_orig;
+                       g_warning(_("Error parsing line %d of the configuration file \"%s\": %s"),lineno,CONFIGURATION_FILE,line);
+                       goto err_append;
+                       }
+               while (*s && isspace(*s)) s++;
+               varcontent_start=s;
+               if (!*s)
+                       goto err_line;
+               while (*s && !isspace(*s)) s++;
+               varcontent_stop=s;
+               varcontent_stop_orig=*varcontent_stop;
+               while (*s && isspace(*s)) s++;
+               if (*s && *s!='#')
+                       goto err_line;
+
+               if (!*varname_start || !*varcontent_start)
+                       goto err_line;
+
+               *varname_stop='\0';
+               *varcontent_stop='\0';
+               if (optarg_verbose)
+                       g_message(_("Parsed configuration variable name \"%s\", value \"%s\""),varname_start,varcontent_start);
+               if (hash_fill)
+                       g_hash_table_insert(
+                                       hash_fill,      /* hash_table */
+                                       g_strdup(varname_start),        /* key */
+                                       g_strdup(varcontent_start));    /* value */
+               if (hash_flush && (value=g_hash_table_lookup(hash_flush,varname_start))) {
+const char *line_new=udpgate_printf_alloca("%s=%s\n",varname_start,value);
+
+                       *varname_stop=varname_stop_orig;
+                       *varcontent_stop=varcontent_stop_orig;
+                       if (strcmp(line,line_new))
+                               modified=TRUE;
+                       *varname_stop=0;
+                       *varcontent_stop=0;
+                       g_string_append(gstring,line_new);
+                       g_hash_table_insert(hash_flushed,g_strdup(varname_start),g_strdup(varcontent_start));
+                       }
+               else
+                       goto err_append;
+               }
+       if (errno) {
+               g_warning(_("Error reading line from the configuration file \"%s\": %s"),CONFIGURATION_FILE,strerror(errno));
+               if (hash_fill)
+                       g_hash_table_destroy(hash_fill);
+               fclose(f);      /* errors ignored */
+               return NULL;
+               }
+       if (fclose(f))
+               g_warning(_("Error closing configuration file \"%s\": %m"),CONFIGURATION_FILE);
+       if (hash_flushed) {
+struct configuration_hash_readwrite_hash_flush_foreach_param param;
+
+               /* Append variable names not yet present in the file */
+               param.gstring=gstring;
+               param.hash_flushed=hash_flushed;
+               param.modified=modified;
+               g_hash_table_foreach(hash_flush,(GHFunc)configuration_hash_readwrite_hash_flush_foreach,&param);
+               g_hash_table_destroy(hash_flushed);
+               hash_flushed=NULL;
+               modified=param.modified;
+               }
+       if (gstring) {
+               if (modified)
+                       configuration_file_write(gstring->str); /* errors ignored */
+               g_string_free(gstring,
+                               TRUE);  /* free_segment */
+               gstring=NULL;
+               }
+       return hash_fill;
+}
+
+static void configuration_read_hash_foreach(const gchar *key,const gchar *value,gpointer user_data)
+{
+       g_return_if_fail(key!=NULL);
+       g_return_if_fail(value!=NULL);
+
+       if (!strcmp(key,"PORT"))
+               optarg_port_set_string(value);  
+       else
+               g_warning(_("Unknown configuration key \"%s\" with value \"%s\" found in the file \"%s\""),
+                               key,value,CONFIGURATION_FILE);
+}
+
+gboolean configuration_read(void)
+{
+GHashTable *hash;
+
+       if (!(hash=configuration_hash_readwrite(NULL)))
+               return FALSE;
+       g_hash_table_foreach(hash,(GHFunc)configuration_read_hash_foreach,NULL);
+       g_hash_table_destroy(hash);
+       return TRUE;
+}
+
+gboolean configuration_write(void)
+{
+GHashTable *hash;
+
+       hash=g_hash_table_new(g_str_hash,g_str_equal);
+       g_hash_table_insert(hash,"PORT",(/* de-const */ gpointer)udpgate_printf_alloca("%d",(int)optarg_port));
+       configuration_hash_readwrite(hash);     /* FIXME: errors ignored */
+       g_hash_table_destroy(hash);
+       return TRUE;
+}
diff --git a/src/configuration.h b/src/configuration.h
new file mode 100644 (file)
index 0000000..f432e75
--- /dev/null
@@ -0,0 +1,35 @@
+/* $Id$
+ * Include file for the persistent configuration
+ * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
+ * 
+ * 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; exactly version 2 of June 1991 is required
+ * 
+ * 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
+ */
+
+
+#ifndef _UDPGATE_CONFIGURATION_H
+#define _UDPGATE_CONFIGURATION_H 1
+
+
+#include <glib/gtypes.h>
+
+
+G_BEGIN_DECLS
+
+gboolean configuration_read(void);
+gboolean configuration_write(void);
+
+G_END_DECLS
+
+
+#endif /* _UDPGATE_CONFIGURATION_H */
index f9d1f5d..a0917e6 100644 (file)
@@ -36,6 +36,7 @@
 #include "main.h"      /* self */
 #include "ui-line.h"
 #include "network.h"
+#include "configuration.h"
 
 #ifdef HAVE_GNOME
 #include "ui-gnome.h"
@@ -142,6 +143,11 @@ gboolean no_gnome;
        bindtextdomain(PACKAGE,LOCALEDIR);
        textdomain(PACKAGE);
 
+       /* Read it before the command-line parsing to get the default value
+        * of 'optarg_port' displayable by 'POPT_ARGFLAG_SHOW_DEFAULT'.
+        */
+       configuration_read();
+
        if (argv[1] && !strcmp(argv[1],"--text"))
                optarg_text=1;
 
@@ -227,5 +233,7 @@ guint handler_id;
        else
                (*ui_interactive)();
 
+       configuration_write();
+
        return EXIT_SUCCESS;
 }
index 83dd99a..725e27a 100644 (file)
@@ -580,6 +580,26 @@ err_sock_gsource_destroy:
        return TRUE;
 }
 
+gboolean optarg_port_set_string(const gchar *port_string)
+{
+char *endp;
+long port_long;
+
+       g_return_val_if_fail(port_string!=NULL,FALSE);
+
+       port_long=strtol(port_string,&endp,0);
+       if (endp && *endp) {
+               g_warning(_("Invalid port specification, offending string: %s"),endp);
+               return FALSE;
+               }
+       if (port_long<1 || port_long>=G_MAXINT || (endp && *endp)) {
+               g_warning(_("Invalid port integer number specification (%ld)"),port_long);
+               return FALSE;
+               }
+       optarg_port=port_long;
+       return TRUE;
+}
+
 gboolean network_stop(void)
 {
 pid_t daemon_pid;
index d6a7797..c341ae7 100644 (file)
@@ -30,6 +30,7 @@ G_BEGIN_DECLS
 
 pid_t is_daemon_running(void);
 gboolean network_start(gint port);
+gboolean optarg_port_set_string(const gchar *string);
 gboolean network_stop(void);
 extern void (*network_notify_hostip)(guint32 hostip_guint32);
 gboolean network_detach(void);
index f0b2bb6..1883b6a 100644 (file)
@@ -105,23 +105,11 @@ void on_AutostartCheckButton_toggled(GtkToggleButton *togglebutton,gpointer user
 
 void on_ButtonStart_clicked(GtkButton *button,gpointer user_data)
 {
-const gchar *port_string;
-char *endp;
-long port_long;
-
        g_return_if_fail(GTK_IS_BUTTON(button));
 
-       port_string=gtk_entry_get_text(PortEntry);
-       port_long=strtol(port_string,&endp,0);
-       if (endp && *endp) {
-               g_warning(_("Invalid port specification, offending string: %s"),endp);
-               return;
-               }
-       if (port_long<1 || port_long>=G_MAXINT || (endp && *endp)) {
-               g_warning(_("Invalid port integer number specification (%ld)"),port_long);
+       if (!optarg_port_set_string(gtk_entry_get_text(PortEntry)))
                return;
-               }
-       network_start(port_long);
+       network_start(optarg_port);
 }
 
 void on_ButtonStop_clicked(GtkButton *button,gpointer user_data)
@@ -175,6 +163,7 @@ GtkWidget *dialog;
 static void ui_gnome_interactive(void)
 {
        gtk_main();
+       optarg_port_set_string(gtk_entry_get_text(PortEntry));  /* update config file */
        network_notify_hostip=NULL;
        g_log_remove_handler(
                        G_LOG_DOMAIN,   /* log_domain; "Captive" */