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 */
37 /* OK, GConf would be nice.
38 * Unfortunately the fully-static build does not support GConf process spawn at
39 * all. Also /etc/sysconfig directory is standard for the daemon services.
44 #define CONFIGURATION_FILE "/etc/sysconfig/udpgate"
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)
71 g_return_val_if_fail(file_content!=NULL,FALSE);
73 if (!(f=fopen(CONFIGURATION_FILE,("w")))) {
74 g_warning(_("Error write opening configuration file \"%s\": %m"),CONFIGURATION_FILE);
77 if (fputs(file_content,f)<0) {
78 g_warning(_("Error writing configuration file \"%s\": %m"),CONFIGURATION_FILE);
79 fclose(f); /* errors ignored */
83 g_warning(_("Error closing configuration file \"%s\": %m"),CONFIGURATION_FILE);
87 struct configuration_hash_readwrite_hash_flush_foreach_param {
89 GHashTable *hash_flushed;
92 static void configuration_hash_readwrite_hash_flush_foreach
93 (const gchar *key,const gchar *value,struct configuration_hash_readwrite_hash_flush_foreach_param *param)
97 g_return_if_fail(key!=NULL);
98 g_return_if_fail(value!=NULL);
99 g_return_if_fail(param!=NULL);
100 g_return_if_fail(param->gstring!=NULL);
101 g_return_if_fail(param->hash_flushed!=NULL);
103 /* Already written out? */
104 if (g_hash_table_lookup(param->hash_flushed,key))
106 if (!(comment=g_hash_table_lookup(configuration_comments_hash_new(),key)))
107 comment=_("*** UNDOCUMENTED ***");
108 g_string_append(param->gstring,udpgate_printf_alloca("# %s: %s\n%s=%s\n",key,comment,key,value));
109 param->modified=TRUE;
111 g_message(_("Appended configuration variable name \"%s\", value \"%s\""),key,value);
114 /* hash_flush==NULL => read info and return new (GHashTable *)
115 * hash_flush!=NULL => update configuration file by this (GHashTable *)
117 /* FIXME: File locking! */
118 static GHashTable *configuration_hash_readwrite(GHashTable *hash_flush)
123 GHashTable *hash_fill;
124 /* Which 'keys' of 'hash_flush' were already written. 'key' must be g_free()able.
125 * Any 'value' is ignored. 'value' must be !=NULL. 'value' must be g_free()able.
127 GHashTable *hash_flushed;
129 gboolean modified=FALSE; /* 'gstring' contains modified value */
130 gboolean already_written=FALSE;
133 if (!(f=fopen(CONFIGURATION_FILE,(!hash_flush ? "r" : "rw")))) {
135 g_warning(_("Error r/o opening configuration file \"%s\": %m"),CONFIGURATION_FILE);
136 if (!hash_flush || already_written)
139 already_written=TRUE;
140 if (!configuration_file_write(_(
141 "# Configuration file for UDPGate - see its man page udpgate(1).\n"
149 hash_fill=configuration_hash_new();
155 hash_flushed=configuration_hash_new();
156 gstring=g_string_new(NULL);
159 while (errno=0,fgets(line,sizeof(line),f)) {
161 char *varname_start,*varname_stop,varname_stop_orig;
162 char *varcontent_start,*varcontent_stop,varcontent_stop_orig;
168 varcontent_stop=NULL;
169 /* Parse: ^\s*([[:alnum:]])\s*=\s*(\S*)\s*$
171 while (*s && isspace(*s)) s++;
172 if (!*s || *s=='#') {
175 g_string_append(gstring,line);
179 while (*s && isalnum(*s)) s++;
181 varname_stop_orig=*varname_stop;
182 while (*s && isspace(*s)) s++;
186 *varname_stop=varname_stop_orig;
188 *varcontent_stop=varcontent_stop_orig;
189 g_warning(_("Error parsing line %d of the configuration file \"%s\": %s"),lineno,CONFIGURATION_FILE,line);
192 while (*s && isspace(*s)) s++;
196 while (*s && !isspace(*s)) s++;
198 varcontent_stop_orig=*varcontent_stop;
199 while (*s && isspace(*s)) s++;
203 if (!*varname_start || !*varcontent_start)
207 *varcontent_stop='\0';
209 g_message(_("Parsed configuration variable name \"%s\", value \"%s\""),varname_start,varcontent_start);
212 hash_fill, /* hash_table */
213 g_strdup(varname_start), /* key */
214 g_strdup(varcontent_start)); /* value */
215 if (hash_flush && (value=g_hash_table_lookup(hash_flush,varname_start))) {
216 const char *line_new=udpgate_printf_alloca("%s=%s\n",varname_start,value);
218 *varname_stop=varname_stop_orig;
219 *varcontent_stop=varcontent_stop_orig;
220 if (strcmp(line,line_new))
224 g_string_append(gstring,line_new);
225 g_hash_table_insert(hash_flushed,g_strdup(varname_start),g_strdup(varcontent_start));
231 g_warning(_("Error reading line from the configuration file \"%s\": %s"),CONFIGURATION_FILE,strerror(errno));
233 g_hash_table_destroy(hash_fill);
234 fclose(f); /* errors ignored */
238 g_warning(_("Error closing configuration file \"%s\": %m"),CONFIGURATION_FILE);
240 struct configuration_hash_readwrite_hash_flush_foreach_param param;
242 /* Append variable names not yet present in the file */
243 param.gstring=gstring;
244 param.hash_flushed=hash_flushed;
245 param.modified=modified;
246 g_hash_table_foreach(hash_flush,(GHFunc)configuration_hash_readwrite_hash_flush_foreach,¶m);
247 g_hash_table_destroy(hash_flushed);
249 modified=param.modified;
253 configuration_file_write(gstring->str); /* errors ignored */
254 g_string_free(gstring,
255 TRUE); /* free_segment */
261 static void configuration_read_hash_foreach(const gchar *key,const gchar *value,gpointer user_data)
263 g_return_if_fail(key!=NULL);
264 g_return_if_fail(value!=NULL);
266 /**/ if (!strcmp(key,"PORT"))
267 optarg_port_set_string(value);
268 else if (!strcmp(key,"LOCATION"))
271 g_warning(_("Unknown configuration key \"%s\" with value \"%s\" found in the file \"%s\""),
272 key,value,CONFIGURATION_FILE);
275 gboolean configuration_read(void)
279 if (!(hash=configuration_hash_readwrite(NULL)))
281 g_hash_table_foreach(hash,(GHFunc)configuration_read_hash_foreach,NULL);
282 g_hash_table_destroy(hash);
286 static void location_insert(GHashTable *hash)
291 g_return_if_fail(hash!=NULL);
293 /* FIXME: Support also argv[0] as a fallback. */
294 got=readlink(LOCATION_LINK,buf,sizeof(buf)-1);
295 if (got<=0 || got>=(int)(sizeof(buf)-1))
298 g_hash_table_insert(hash,"LOCATION",g_strdup(buf));
301 gboolean configuration_write(void)
305 hash=g_hash_table_new_full(g_str_hash,g_str_equal,
306 (GDestroyNotify)NULL, /* key_destroy_func */
307 (GDestroyNotify)g_free); /* value_destroy_func; of g_strdup() strings */
308 g_hash_table_insert(hash,"PORT",g_strdup_printf("%d",(int)optarg_port));
309 location_insert(hash);
310 configuration_hash_readwrite(hash); /* FIXME: errors ignored */
311 g_hash_table_destroy(hash);