1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
Documentation for gconfig
gconfig is a configuration file parser. It is generic in the sense that it can
be used for different applications that need different configuration keywords
and value. There is however a fixed syntax.
The basic syntax is
option-name value
or
option-name = value
where white space is used as delimiters, where you can have any amount of
whitespace (spaces and tabs) where there is a space above. option-name cannot
contain spaces. A value can contain spaces if the value is quoted. E.g.
option-name = "option value"
There is a special keyword "include" used for including config files.
Anywhere in a config file one can write "include filespec". At that
point the entire content of a file pointed to by filespec will be
inserted. When the end of the file is read, the config parser will
continue with whatever is after include. "filespec" can use UNIX
shell type globbing, so you may do e.g. "include /etc/gconf.d/*" to
include everything in that directory. This will behave exactly like
including each of the files separately (in alphabetic order).
In addition to this, one may also group options together into
blocks, where blocks also have names.
A block has the syntax
type name {
option-name value
option-name value
...
}
The application specifies which block types are valid, and which
option-names and syntaxes can be used for each of the types.
The available option syntaxes (or types) are String, Integer and
Boolean.
Usually the same option-name cannot be repeated (unless in different
blocks). For Strings one can specify whether it is allowed or not.
Also, for strings one can use hex notation for bytes. This works
similar to URL hex escaping. E.g. a space can be written as "%20".
radsecproxy uses gconfig and shows what is possible. Here is an
example extracted from getmainconfig() from radsecproxy.
struct gconffile *cfs;
cfs = openconfigfile(configfile);
if (!getgenericconfig(&cfs, NULL,
"ListenUDP", CONF_MSTR, &listenudp,
"LogDestination", CONF_STR, &logdestination,
"LogLevel", CONF_LINT, &loglevel,
"LoopPrevention", CONF_BLN, &loopprevention,
"Client", CONF_CBK, confclient_cb, NULL,
NULL
))
debugx(1, DBG_ERR, "configuration error"); /* exit */
/* parsing successful, continue normal operations */
The arguments to getgenericconfig are as follows. First there is a
gconffile struct, this is normally a single file, but as we discuss later,
it can also be a list of files (or data). The second parameter should be
NULL when not in a block, or the block name if inside one. This is only
used for logging.
After this one lists all the legal option and block names that are allowed.
For each option one specifies a name followed by a syntax type and a pointer to
the variable that will hold the value. The syntax types are CONF_STR for a
string, CONF_MSTR for string when the option can be used multiple times,
CONF_LINT for Integer (long int), CONF_BLN for Boolean ("on" or "off"). For
blocks one uses CONF_CBK followed by a callback function for handling the
block, and a pointer to userdata that can be passed to the callback. Normally
this would just be set to NULL.
We will now discuss each of the syntax types and blocks in more detail.
CONF_STR:
This is used for strings. The value argument must be of type "char **". If
the option is found in the config, it will allocate memory for a null-terminated
copy of the string and return a pointer to this. The value argument is not
modified if the option is not used. In the example above, one might do
"char *logdestination = NULL". One then uses &logdestination for the
argument, and can check for NULL to see whether the option was present.
CONF_MSTR:
This is used for strings when the option can be used multiple times. It
will return an array of strings in the order they are found in the config.
The value argument must be of type "char ***". One might do
"char **listenudp = NULL", and use &listenudp for the argument. Memory will
be allocated for the array and the strings if the option is used. If no
options are found, the value is unchanged. If there is at least one value,
one will get an array of strings, and the last element of the array will be
set to NULL.
CONF_LINT:
The option value argument must be of type "long int *". If the option is
present, the value will be set, else it will not be modified.
CONF_BLN:
The option value argument must be of type "uint8_t *". It is set to 0 or 1
for "off" or "on" resp. It is only modified if the option is found.
CONF_CBK:
This is a block where one uses a call back. Here is an example callback:
int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
*val) {
char *type = NULL, *host = NULL;
debug(DBG_DBG, "confclient_cb called for %s", block);
if (!getgenericconfig(cf, block,
"type", CONF_STR, &type,
"host", CONF_STR, &host,
NULL
))
debugx(1, DBG_ERR, "configuration error"); /* exit */
/* parsing successful, continue normal operations */
Here cf is the list of config files we're using, arg is a pointer to
userdata (the parameter after the name of the callback function, often
this is just set to NULL). opt is the type of block, in this case it will
be set to "client". val is the value of the block. E.g. if the config said
client example {
type udp
host 1.2.3.4
}
then val will be set to "example". The string block is set to
"client example" (opt val) and is normally just used for logging.
Note that the opt, val and optval parameters to the callback will all be
freed when the callback function exits.
The gconffile struct can hold a list of config files. Typically one would
just use cfs = openconfigfile(configfile) as above. But one can use any of
the ones below. It can be used to create a new struct (a pointer to NULL),
or a pointer to an existing struct where new files are pushed (as a stack).
The latest pushed is used first, when that is used it continues reading
from the next.
int pushgconfdata(struct gconffile **cf, const char *data);
This can be used to read config stored in memory
FILE *pushgconfpath(struct gconffile **cf, const char *path);
Here one specifies a file path (no globbing)
FILE *pushgconfpaths(struct gconffile **cf, const char *path);
Here one specifies a filespec with globbing. In case of multiple files,
they are pushed in reverse alphabetical order so that the alphabetically
first file is read first.
FILE *pushgconffile(struct gconffile **cf, FILE *file, const char *description);
This can be used for reading config from an already open file descriptor.
For a complete example, see radsecproxy.
|