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>
27 #include <glib/gutils.h>
34 #include "configuration.h" /* self */
40 /* OK, FIXME: GConf would be nice.
41 * Unfortunately the fully-static build does not support GConf process spawn at
47 #define LOCATION_LINK "/proc/self/exe" /* for Linux kernel */
50 static G_CONST_RETURN gchar *configuration_pathname(void)
52 static const gchar *static_pathname;
54 return pathname_find(&static_pathname,
55 G_STRINGIFY(SYSCONFDIR) "/sysconfig",PACKAGE,
56 G_STRINGIFY(SYSCONFDIR) "/default",PACKAGE,
57 G_STRINGIFY(SYSCONFDIR) "",PACKAGE,
58 /* g_get_home_dir() may return NULL and terminate the list prematurely. */
59 g_get_home_dir(),"." PACKAGE "rc",
63 static GHashTable *configuration_hash_new(void)
65 return g_hash_table_new_full(g_str_hash,g_str_equal,
66 (GDestroyNotify)g_free, /* key_destroy_func; of g_strdup() strings */
67 (GDestroyNotify)g_free); /* value_destroy_func; of g_strdup() strings */
70 static GHashTable *configuration_comments_hash_new(void)
72 static GHashTable *hash;
75 hash=g_hash_table_new(g_str_hash,g_str_equal);
76 g_hash_table_insert(hash,"PORT",_("Local UDP port"));
77 g_hash_table_insert(hash,"LOCATION",_("Binary location pathname"));
82 static gboolean configuration_file_write(const gchar *file_content)
85 const gchar *pathname;
87 g_return_val_if_fail(file_content!=NULL,FALSE);
89 if (!(pathname=configuration_pathname()))
91 if (!(f=fopen(pathname,"w"))) {
92 g_warning(_("Error write opening configuration file \"%s\": %m"),pathname);
95 if (fputs(file_content,f)<0) {
96 g_warning(_("Error writing configuration file \"%s\": %m"),pathname);
97 fclose(f); /* errors ignored */
101 g_warning(_("Error closing configuration file \"%s\": %m"),pathname);
105 struct configuration_hash_readwrite_hash_flush_foreach_param {
107 GHashTable *hash_flushed;
110 static void configuration_hash_readwrite_hash_flush_foreach
111 (const gchar *key,const gchar *value,struct configuration_hash_readwrite_hash_flush_foreach_param *param)
113 const gchar *comment;
115 g_return_if_fail(key!=NULL);
116 g_return_if_fail(value!=NULL);
117 g_return_if_fail(param!=NULL);
118 g_return_if_fail(param->gstring!=NULL);
119 g_return_if_fail(param->hash_flushed!=NULL);
121 /* Already written out? */
122 if (g_hash_table_lookup(param->hash_flushed,key))
124 if (!(comment=g_hash_table_lookup(configuration_comments_hash_new(),key)))
125 comment=_("*** UNDOCUMENTED ***");
126 g_string_append(param->gstring,udpgate_printf_alloca("# %s: %s\n%s=%s\n",key,comment,key,value));
127 param->modified=TRUE;
129 g_message(_("Appended configuration variable name \"%s\", value \"%s\""),key,value);
132 /* hash_flush==NULL => read info and return new (GHashTable *)
133 * hash_flush!=NULL => update configuration file by this (GHashTable *)
135 /* FIXME: File locking! */
136 static GHashTable *configuration_hash_readwrite(GHashTable *hash_flush)
141 GHashTable *hash_fill;
142 /* Which 'keys' of 'hash_flush' were already written. 'key' must be g_free()able.
143 * Any 'value' is ignored. 'value' must be !=NULL. 'value' must be g_free()able.
145 GHashTable *hash_flushed;
147 gboolean modified=FALSE; /* 'gstring' contains modified value */
148 gboolean already_written=FALSE;
149 const gchar *pathname;
151 if (!(pathname=configuration_pathname()))
154 if (!(f=fopen(pathname,(!hash_flush ? "r" : "rw")))) {
156 g_warning(_("Error r/o opening configuration file \"%s\": %m"),pathname);
157 if (!hash_flush || already_written)
160 already_written=TRUE;
161 if (!configuration_file_write(_(
162 "# Configuration file for UDPGate - see its man page udpgate(1).\n"
170 hash_fill=configuration_hash_new();
176 hash_flushed=configuration_hash_new();
177 gstring=g_string_new(NULL);
180 while (errno=0,fgets(line,sizeof(line),f)) {
182 char *varname_start,*varname_stop,varname_stop_orig;
183 char *varcontent_start,*varcontent_stop,varcontent_stop_orig=0 /* shut up GCC */;
189 varcontent_stop=NULL;
190 /* Parse: ^\s*([[:alnum:]])\s*=\s*(\S*)\s*$
192 while (*s && isspace(*s)) s++;
193 if (!*s || *s=='#') {
196 g_string_append(gstring,line);
200 while (*s && isalnum(*s)) s++;
202 varname_stop_orig=*varname_stop;
203 while (*s && isspace(*s)) s++;
207 *varname_stop=varname_stop_orig;
209 *varcontent_stop=varcontent_stop_orig;
210 g_warning(_("Error parsing line %d of the configuration file \"%s\": %s"),lineno,pathname,line);
213 while (*s && isspace(*s)) s++;
217 while (*s && !isspace(*s)) s++;
219 varcontent_stop_orig=*varcontent_stop;
220 while (*s && isspace(*s)) s++;
224 if (!*varname_start || !*varcontent_start)
228 *varcontent_stop='\0';
230 g_message(_("Parsed configuration variable name \"%s\", value \"%s\""),varname_start,varcontent_start);
233 hash_fill, /* hash_table */
234 g_strdup(varname_start), /* key */
235 g_strdup(varcontent_start)); /* value */
236 if (hash_flush && (value=g_hash_table_lookup(hash_flush,varname_start))) {
237 const char *line_new=udpgate_printf_alloca("%s=%s\n",varname_start,value);
239 *varname_stop=varname_stop_orig;
240 *varcontent_stop=varcontent_stop_orig;
241 if (strcmp(line,line_new))
245 g_string_append(gstring,line_new);
246 g_hash_table_insert(hash_flushed,g_strdup(varname_start),g_strdup(varcontent_start));
252 g_warning(_("Error reading line from the configuration file \"%s\": %s"),pathname,strerror(errno));
254 g_hash_table_destroy(hash_fill);
255 fclose(f); /* errors ignored */
259 g_warning(_("Error closing configuration file \"%s\": %m"),pathname);
261 struct configuration_hash_readwrite_hash_flush_foreach_param param;
263 /* Append variable names not yet present in the file */
264 param.gstring=gstring;
265 param.hash_flushed=hash_flushed;
266 param.modified=modified;
267 g_hash_table_foreach(hash_flush,(GHFunc)configuration_hash_readwrite_hash_flush_foreach,¶m);
268 g_hash_table_destroy(hash_flushed);
270 modified=param.modified;
274 configuration_file_write(gstring->str); /* errors ignored */
275 g_string_free(gstring,
276 TRUE); /* free_segment */
282 static void configuration_read_hash_foreach(const gchar *key,const gchar *value,gpointer user_data)
284 g_return_if_fail(key!=NULL);
285 g_return_if_fail(value!=NULL);
287 /**/ if (!strcmp(key,"PORT"))
288 optarg_port_set_string(value);
289 else if (!strcmp(key,"LOCATION"))
292 g_warning(_("Unknown configuration key \"%s\" with value \"%s\" found in the file \"%s\""),
293 key,value,configuration_pathname());
296 gboolean configuration_read(void)
300 if (!(hash=configuration_hash_readwrite(NULL)))
302 g_hash_table_foreach(hash,(GHFunc)configuration_read_hash_foreach,NULL);
303 g_hash_table_destroy(hash);
307 static void location_insert(GHashTable *hash)
312 g_return_if_fail(hash!=NULL);
314 /* FIXME: Support also argv[0] as a fallback. */
315 got=readlink(LOCATION_LINK,buf,sizeof(buf)-1);
316 if (got<=0 || got>=(int)(sizeof(buf)-1))
319 g_hash_table_insert(hash,"LOCATION",g_strdup(buf));
322 gboolean configuration_write(void)
326 hash=g_hash_table_new_full(g_str_hash,g_str_equal,
327 (GDestroyNotify)NULL, /* key_destroy_func */
328 (GDestroyNotify)g_free); /* value_destroy_func; of g_strdup() strings */
329 g_hash_table_insert(hash,"PORT",g_strdup_printf("%d",(int)optarg_port));
330 location_insert(hash);
331 configuration_hash_readwrite(hash); /* FIXME: errors ignored */
332 g_hash_table_destroy(hash);