2 * UDP Gateway persistent configuration
3 * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <glib/gmessages.h>
23 #include <glib/ghash.h>
24 #include <glib/gstring.h>
25 #include <glib/gmem.h>
26 #include <glib/gstrfuncs.h>
33 #include "configuration.h" /* self */
39 /* OK, FIXME: GConf would be nice.
40 * Unfortunately the fully-static build does not support GConf process spawn at
46 #define LOCATION_LINK "/proc/self/exe" /* for Linux kernel */
49 static G_CONST_RETURN gchar *configuration_pathname(void)
51 static const gchar *static_pathname;
53 return pathname_find(&static_pathname,
54 G_STRINGIFY(SYSCONFDIR) "/sysconfig",PACKAGE,
55 G_STRINGIFY(SYSCONFDIR) "/default",PACKAGE,
56 G_STRINGIFY(SYSCONFDIR) "",PACKAGE,
57 /* 'getenv("HOME")' may return NULL and terminate the list prematurely. */
58 getenv("HOME"),"." PACKAGE "rc",
62 static GHashTable *configuration_hash_new(void)
64 return g_hash_table_new_full(g_str_hash,g_str_equal,
65 (GDestroyNotify)g_free, /* key_destroy_func; of g_strdup() strings */
66 (GDestroyNotify)g_free); /* value_destroy_func; of g_strdup() strings */
69 static GHashTable *configuration_comments_hash_new(void)
71 static GHashTable *hash;
74 hash=g_hash_table_new(g_str_hash,g_str_equal);
75 g_hash_table_insert(hash,"PORT",_("Local UDP port"));
76 g_hash_table_insert(hash,"LOCATION",_("Binary location pathname"));
81 static gboolean configuration_file_write(const gchar *file_content)
84 const gchar *pathname;
86 g_return_val_if_fail(file_content!=NULL,FALSE);
88 if (!(pathname=configuration_pathname()))
90 if (!(f=fopen(pathname,"w"))) {
91 g_warning(_("Error write opening configuration file \"%s\": %m"),pathname);
94 if (fputs(file_content,f)<0) {
95 g_warning(_("Error writing configuration file \"%s\": %m"),pathname);
96 fclose(f); /* errors ignored */
100 g_warning(_("Error closing configuration file \"%s\": %m"),pathname);
104 struct configuration_hash_readwrite_hash_flush_foreach_param {
106 GHashTable *hash_flushed;
109 static void configuration_hash_readwrite_hash_flush_foreach
110 (const gchar *key,const gchar *value,struct configuration_hash_readwrite_hash_flush_foreach_param *param)
112 const gchar *comment;
114 g_return_if_fail(key!=NULL);
115 g_return_if_fail(value!=NULL);
116 g_return_if_fail(param!=NULL);
117 g_return_if_fail(param->gstring!=NULL);
118 g_return_if_fail(param->hash_flushed!=NULL);
120 /* Already written out? */
121 if (g_hash_table_lookup(param->hash_flushed,key))
123 if (!(comment=g_hash_table_lookup(configuration_comments_hash_new(),key)))
124 comment=_("*** UNDOCUMENTED ***");
125 g_string_append(param->gstring,udpgate_printf_alloca("# %s: %s\n%s=%s\n",key,comment,key,value));
126 param->modified=TRUE;
128 g_message(_("Appended configuration variable name \"%s\", value \"%s\""),key,value);
131 /* hash_flush==NULL => read info and return new (GHashTable *)
132 * hash_flush!=NULL => update configuration file by this (GHashTable *)
134 /* FIXME: File locking! */
135 static GHashTable *configuration_hash_readwrite(GHashTable *hash_flush)
140 GHashTable *hash_fill;
141 /* Which 'keys' of 'hash_flush' were already written. 'key' must be g_free()able.
142 * Any 'value' is ignored. 'value' must be !=NULL. 'value' must be g_free()able.
144 GHashTable *hash_flushed;
146 gboolean modified=FALSE; /* 'gstring' contains modified value */
147 gboolean already_written=FALSE;
148 const gchar *pathname;
150 if (!(pathname=configuration_pathname()))
153 if (!(f=fopen(pathname,(!hash_flush ? "r" : "rw")))) {
155 g_warning(_("Error r/o opening configuration file \"%s\": %m"),pathname);
156 if (!hash_flush || already_written)
159 already_written=TRUE;
160 if (!configuration_file_write(_(
161 "# Configuration file for UDPGate - see its man page udpgate(1).\n"
169 hash_fill=configuration_hash_new();
175 hash_flushed=configuration_hash_new();
176 gstring=g_string_new(NULL);
179 while (errno=0,fgets(line,sizeof(line),f)) {
181 char *varname_start,*varname_stop,varname_stop_orig;
182 char *varcontent_start,*varcontent_stop,varcontent_stop_orig=0 /* shut up GCC */;
188 varcontent_stop=NULL;
189 /* Parse: ^\s*([[:alnum:]])\s*=\s*(\S*)\s*$
191 while (*s && isspace(*s)) s++;
192 if (!*s || *s=='#') {
195 g_string_append(gstring,line);
199 while (*s && isalnum(*s)) s++;
201 varname_stop_orig=*varname_stop;
202 while (*s && isspace(*s)) s++;
206 *varname_stop=varname_stop_orig;
208 *varcontent_stop=varcontent_stop_orig;
209 g_warning(_("Error parsing line %d of the configuration file \"%s\": %s"),lineno,pathname,line);
212 while (*s && isspace(*s)) s++;
216 while (*s && !isspace(*s)) s++;
218 varcontent_stop_orig=*varcontent_stop;
219 while (*s && isspace(*s)) s++;
223 if (!*varname_start || !*varcontent_start)
227 *varcontent_stop='\0';
229 g_message(_("Parsed configuration variable name \"%s\", value \"%s\""),varname_start,varcontent_start);
232 hash_fill, /* hash_table */
233 g_strdup(varname_start), /* key */
234 g_strdup(varcontent_start)); /* value */
235 if (hash_flush && (value=g_hash_table_lookup(hash_flush,varname_start))) {
236 const char *line_new=udpgate_printf_alloca("%s=%s\n",varname_start,value);
238 *varname_stop=varname_stop_orig;
239 *varcontent_stop=varcontent_stop_orig;
240 if (strcmp(line,line_new))
244 g_string_append(gstring,line_new);
245 g_hash_table_insert(hash_flushed,g_strdup(varname_start),g_strdup(varcontent_start));
251 g_warning(_("Error reading line from the configuration file \"%s\": %s"),pathname,strerror(errno));
253 g_hash_table_destroy(hash_fill);
254 fclose(f); /* errors ignored */
258 g_warning(_("Error closing configuration file \"%s\": %m"),pathname);
260 struct configuration_hash_readwrite_hash_flush_foreach_param param;
262 /* Append variable names not yet present in the file */
263 param.gstring=gstring;
264 param.hash_flushed=hash_flushed;
265 param.modified=modified;
266 g_hash_table_foreach(hash_flush,(GHFunc)configuration_hash_readwrite_hash_flush_foreach,¶m);
267 g_hash_table_destroy(hash_flushed);
269 modified=param.modified;
273 configuration_file_write(gstring->str); /* errors ignored */
274 g_string_free(gstring,
275 TRUE); /* free_segment */
281 static void configuration_read_hash_foreach(const gchar *key,const gchar *value,gpointer user_data)
283 g_return_if_fail(key!=NULL);
284 g_return_if_fail(value!=NULL);
286 /**/ if (!strcmp(key,"PORT"))
287 optarg_port_set_string(value);
288 else if (!strcmp(key,"LOCATION"))
291 g_warning(_("Unknown configuration key \"%s\" with value \"%s\" found in the file \"%s\""),
292 key,value,configuration_pathname());
295 gboolean configuration_read(void)
299 if (!(hash=configuration_hash_readwrite(NULL)))
301 g_hash_table_foreach(hash,(GHFunc)configuration_read_hash_foreach,NULL);
302 g_hash_table_destroy(hash);
306 static void location_insert(GHashTable *hash)
311 g_return_if_fail(hash!=NULL);
313 /* FIXME: Support also argv[0] as a fallback. */
314 got=readlink(LOCATION_LINK,buf,sizeof(buf)-1);
315 if (got<=0 || got>=(int)(sizeof(buf)-1))
318 g_hash_table_insert(hash,"LOCATION",g_strdup(buf));
321 gboolean configuration_write(void)
325 hash=g_hash_table_new_full(g_str_hash,g_str_equal,
326 (GDestroyNotify)NULL, /* key_destroy_func */
327 (GDestroyNotify)g_free); /* value_destroy_func; of g_strdup() strings */
328 g_hash_table_insert(hash,"PORT",g_strdup_printf("%d",(int)optarg_port));
329 location_insert(hash);
330 configuration_hash_readwrite(hash); /* FIXME: errors ignored */
331 g_hash_table_destroy(hash);