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>
29 #include <glib/gstrfuncs.h>
32 #include "configuration.h" /* self */
33 #include "configuration-pathname.h"
38 /* OK, FIXME: GConf would be nice.
39 * Unfortunately the fully-static build does not support GConf process spawn at
45 #define LOCATION_LINK "/proc/self/exe" /* for Linux kernel */
48 static GHashTable *configuration_hash_new(void)
50 return g_hash_table_new_full(g_str_hash,g_str_equal,
51 (GDestroyNotify)g_free, /* key_destroy_func; of g_strdup() strings */
52 (GDestroyNotify)g_free); /* value_destroy_func; of g_strdup() strings */
55 static GHashTable *configuration_comments_hash_new(void)
57 static GHashTable *hash;
60 hash=g_hash_table_new(g_str_hash,g_str_equal);
61 g_hash_table_insert(hash,"PORT",_("Local UDP port"));
62 g_hash_table_insert(hash,"LOCATION",_("Binary location pathname"));
67 static gboolean configuration_file_write(const gchar *file_content)
70 const gchar *pathname;
72 g_return_val_if_fail(file_content!=NULL,FALSE);
74 if (!(pathname=configuration_pathname()))
76 if (!(f=fopen(pathname,"w"))) {
77 g_warning(_("Error write opening configuration file \"%s\": %m"),pathname);
80 if (fputs(file_content,f)<0) {
81 g_warning(_("Error writing configuration file \"%s\": %m"),pathname);
82 fclose(f); /* errors ignored */
86 g_warning(_("Error closing configuration file \"%s\": %m"),pathname);
90 struct configuration_hash_readwrite_hash_flush_foreach_param {
92 GHashTable *hash_flushed;
95 static void configuration_hash_readwrite_hash_flush_foreach
96 (const gchar *key,const gchar *value,struct configuration_hash_readwrite_hash_flush_foreach_param *param)
100 g_return_if_fail(key!=NULL);
101 g_return_if_fail(value!=NULL);
102 g_return_if_fail(param!=NULL);
103 g_return_if_fail(param->gstring!=NULL);
104 g_return_if_fail(param->hash_flushed!=NULL);
106 /* Already written out? */
107 if (g_hash_table_lookup(param->hash_flushed,key))
109 if (!(comment=g_hash_table_lookup(configuration_comments_hash_new(),key)))
110 comment=_("*** UNDOCUMENTED ***");
111 g_string_append(param->gstring,udpgate_printf_alloca("# %s: %s\n%s=%s\n",key,comment,key,value));
112 param->modified=TRUE;
114 g_message(_("Appended configuration variable name \"%s\", value \"%s\""),key,value);
117 /* hash_flush==NULL => read info and return new (GHashTable *)
118 * hash_flush!=NULL => update configuration file by this (GHashTable *)
120 /* FIXME: File locking! */
121 static GHashTable *configuration_hash_readwrite(GHashTable *hash_flush)
126 GHashTable *hash_fill;
127 /* Which 'keys' of 'hash_flush' were already written. 'key' must be g_free()able.
128 * Any 'value' is ignored. 'value' must be !=NULL. 'value' must be g_free()able.
130 GHashTable *hash_flushed;
132 gboolean modified=FALSE; /* 'gstring' contains modified value */
133 gboolean already_written=FALSE;
134 const gchar *pathname;
136 if (!(pathname=configuration_pathname()))
139 if (!(f=fopen(pathname,(!hash_flush ? "r" : "rw")))) {
141 g_warning(_("Error r/o opening configuration file \"%s\": %m"),pathname);
142 if (!hash_flush || already_written)
145 already_written=TRUE;
146 if (!configuration_file_write(_(
147 "# Configuration file for UDPGate - see its man page udpgate(1).\n"
155 hash_fill=configuration_hash_new();
161 hash_flushed=configuration_hash_new();
162 gstring=g_string_new(NULL);
165 while (errno=0,fgets(line,sizeof(line),f)) {
167 char *varname_start,*varname_stop,varname_stop_orig;
168 char *varcontent_start,*varcontent_stop,varcontent_stop_orig=0 /* shut up GCC */;
174 varcontent_stop=NULL;
175 /* Parse: ^\s*([[:alnum:]])\s*=\s*(\S*)\s*$
177 while (*s && isspace(*s)) s++;
178 if (!*s || *s=='#') {
181 g_string_append(gstring,line);
185 while (*s && isalnum(*s)) s++;
187 varname_stop_orig=*varname_stop;
188 while (*s && isspace(*s)) s++;
192 *varname_stop=varname_stop_orig;
194 *varcontent_stop=varcontent_stop_orig;
195 g_warning(_("Error parsing line %d of the configuration file \"%s\": %s"),lineno,pathname,line);
198 while (*s && isspace(*s)) s++;
202 while (*s && !isspace(*s)) s++;
204 varcontent_stop_orig=*varcontent_stop;
205 while (*s && isspace(*s)) s++;
209 if (!*varname_start || !*varcontent_start)
213 *varcontent_stop='\0';
215 g_message(_("Parsed configuration variable name \"%s\", value \"%s\""),varname_start,varcontent_start);
218 hash_fill, /* hash_table */
219 g_strdup(varname_start), /* key */
220 g_strdup(varcontent_start)); /* value */
221 if (hash_flush && (value=g_hash_table_lookup(hash_flush,varname_start))) {
222 const char *line_new=udpgate_printf_alloca("%s=%s\n",varname_start,value);
224 *varname_stop=varname_stop_orig;
225 *varcontent_stop=varcontent_stop_orig;
226 if (strcmp(line,line_new))
230 g_string_append(gstring,line_new);
231 g_hash_table_insert(hash_flushed,g_strdup(varname_start),g_strdup(varcontent_start));
237 g_warning(_("Error reading line from the configuration file \"%s\": %s"),pathname,strerror(errno));
239 g_hash_table_destroy(hash_fill);
240 fclose(f); /* errors ignored */
244 g_warning(_("Error closing configuration file \"%s\": %m"),pathname);
246 struct configuration_hash_readwrite_hash_flush_foreach_param param;
248 /* Append variable names not yet present in the file */
249 param.gstring=gstring;
250 param.hash_flushed=hash_flushed;
251 param.modified=modified;
252 g_hash_table_foreach(hash_flush,(GHFunc)configuration_hash_readwrite_hash_flush_foreach,¶m);
253 g_hash_table_destroy(hash_flushed);
255 modified=param.modified;
259 configuration_file_write(gstring->str); /* errors ignored */
260 g_string_free(gstring,
261 TRUE); /* free_segment */
267 static void configuration_read_hash_foreach(const gchar *key,const gchar *value,gpointer user_data)
269 g_return_if_fail(key!=NULL);
270 g_return_if_fail(value!=NULL);
272 /**/ if (!strcmp(key,"PORT"))
273 optarg_port_set_string(value);
274 else if (!strcmp(key,"LOCATION"))
277 g_warning(_("Unknown configuration key \"%s\" with value \"%s\" found in the file \"%s\""),
278 key,value,configuration_pathname());
281 gboolean configuration_read(void)
285 if (!(hash=configuration_hash_readwrite(NULL)))
287 g_hash_table_foreach(hash,(GHFunc)configuration_read_hash_foreach,NULL);
288 g_hash_table_destroy(hash);
292 static void location_insert(GHashTable *hash)
297 g_return_if_fail(hash!=NULL);
299 /* FIXME: Support also argv[0] as a fallback. */
300 got=readlink(LOCATION_LINK,buf,sizeof(buf)-1);
301 if (got<=0 || got>=(int)(sizeof(buf)-1))
304 g_hash_table_insert(hash,"LOCATION",g_strdup(buf));
307 gboolean configuration_write(void)
311 hash=g_hash_table_new_full(g_str_hash,g_str_equal,
312 (GDestroyNotify)NULL, /* key_destroy_func */
313 (GDestroyNotify)g_free); /* value_destroy_func; of g_strdup() strings */
314 g_hash_table_insert(hash,"PORT",g_strdup_printf("%d",(int)optarg_port));
315 location_insert(hash);
316 configuration_hash_readwrite(hash); /* FIXME: errors ignored */
317 g_hash_table_destroy(hash);