/* $Id: plugin_i2c_sensors.c,v 1.7 2004/01/30 20:57:56 reinelt Exp $
*
* I2C sensors plugin
*
* Copyright 2003,2004 Xavier Vello <xavier66@free.fr>
*
* This file is part of LCD4Linux.
*
* LCD4Linux is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* LCD4Linux is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* $Log: plugin_i2c_sensors.c,v $
* Revision 1.7 2004/01/30 20:57:56 reinelt
* HD44780 patch from Martin Hejl
* dmalloc integrated
*
* Revision 1.6 2004/01/30 07:12:35 reinelt
* HD44780 busy-flag support from Martin Hejl
* loadavg() uClibc replacement from Martin Heyl
* round() uClibc replacement from Martin Hejl
* warning in i2c_sensors fixed
* [
*
* Revision 1.5 2004/01/29 05:55:30 reinelt
* check for /sys mounted
*
* Revision 1.4 2004/01/29 04:40:02 reinelt
* every .c file includes "config.h" now
*
* Revision 1.3 2004/01/27 08:13:39 reinelt
* ported PPP token to plugin_ppp
*
* Revision 1.2 2004/01/27 05:06:10 reinelt
* i2c update from Xavier
*
* Revision 1.1 2004/01/10 17:36:56 reinelt
*
* I2C Sensors plugin from Xavier added
*
*
*/
/*
* exported functions:
*
* int plugin_init_i2c_sensors (void)
* adds function i2c_sensors() to retrieve informations from
* the i2c sensors via the sysfs interface of 2.6.x kernels
*
* -- WARNING --
* This plugin is only for kernel series 2.5/2.6 and higher !
* It uses the new sysfs pseudo-filesystem that you can mount with:
* mount -t sysfs none /sys
*
* -- WARNING #2 --
* This plugin should detect where your sensors are at startup.
* If you can't get any token to work, ensure you don't get
* an error message with "lcd4linux -Fvvv".
*
* If so, try to force the path to your sensors in the conf like this :
* i2c_sensors-path '/sys/bus/i2c/devices/0-6000/'
* (replace 0-6000 with the appropriate dir)
*
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include "debug.h"
#include "plugin.h"
#include "cfg.h"
#include "hash.h"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif
static char *path=NULL;
static HASH I2Csensors = { 0, };
static int parse_i2c_sensors(RESULT *arg)
{
double value;
char val[32];
char buffer[32];
char *key=R2S(arg);
char file[64];
FILE *stream;
// construct absolute path to the file to read
strcpy(file, path);
strcat(file, key);
// read of file to buffer
stream=fopen(file, "r");
if (stream==NULL) {
error ("fopen(%s) failed",file);
return -1;
}
fgets (buffer, sizeof(buffer), stream);
fclose (stream);
if (!buffer) {
error ("%s empty ?!",file);
return -1;
}
// now the formating stuff, depending on the file :
// Some values must be divided by 1000, the others
// are parsed directly (we just remove the \n).
if (!strncmp(key, "temp_", 5) ||
!strncmp(key, "curr_", 5) ||
!strncmp(key, "in_", 3) ||
!strncmp(key, "vid", 3)) {
value = strtod(buffer, NULL);
// FIXME: any way to do this without converting to double ?
value /= 1000.0;
sprintf(val, "%f", value);
} else {
sprintf(val, "%s", buffer);
// we supress this nasty \n at the end
val[strlen(valpre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long *//* $Id: cfg.c,v 1.39 2004/03/11 06:39:58 reinelt Exp $^
*
* config file stuff
*
* Copyright 1999, 2000 Michael Reinelt <reinelt@eunet.at>
* Copyright 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
*
* This file is part of LCD4Linux.
*
* LCD4Linux is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* LCD4Linux is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* $Log: cfg.c,v $
* Revision 1.39 2004/03/11 06:39:58 reinelt
* big patch from Martin:
* - reuse filehandles
* - memory leaks fixed
* - earlier busy-flag checking with HD44780
* - reuse memory for strings in RESULT and hash
* - netdev_fast to wavid time-consuming regex
*
* Revision 1.38 2004/03/08 16:26:26 reinelt
* re-introduced \nnn (octal) characters in strings
* text widgets can have a 'update' speed of 0 which means 'never'
* (may be used for static content)
*
* Revision 1.37 2004/03/06 20:31:16 reinelt
* Complete rewrite of the evaluator to get rid of the code
* from mark Morley (because of license issues).
* The new Evaluator does a pre-compile of expressions, and
* stores them in trees. Therefore it should be reasonable faster...
*
* Revision 1.36 2004/03/03 03:47:04 reinelt
* big patch from Martin Hejl:
* - use qprintf() where appropriate
* - save CPU cycles on gettimeofday()
* - add quit() functions to free allocated memory
* - fixed lots of memory leaks
*
* Revision 1.35 2004/03/01 04:29:51 reinelt
* cfg_number() returns -1 on error, 0 if value not found (but default val used),
* and 1 if value was used from the configuration.
* HD44780 driver adopted to new cfg_number()
* Crystalfontz 631 driver nearly finished
*
* Revision 1.34 2004/02/18 06:39:20 reinelt
* T6963 driver for graphic displays finished
*
* Revision 1.33 2004/02/01 18:08:50 reinelt
* removed strtok() from layout processing (took me hours to find this bug)
* further strtok() removind should be done!
*
* Revision 1.32 2004/01/30 20:57:55 reinelt
* HD44780 patch from Martin Hejl
* dmalloc integrated
*
* Revision 1.31 2004/01/29 04:40:02 reinelt
* every .c file includes "config.h" now
*
* Revision 1.30 2004/01/22 07:57:45 reinelt
* several bugs fixed where segfaulting on layout>display
* Crystalfontz driver optimized, 632 display already works
*
* Revision 1.29 2004/01/18 06:54:08 reinelt
* bug in expr.c fixed (thanks to Xavier)
* some progress with /proc/stat parsing
*
* Revision 1.28 2004/01/16 05:04:53 reinelt
* started plugin proc_stat which should parse /proc/stat
* which again is a paint in the a**
* thinking over implementation methods of delta functions
* (CPU load, ...)
*
* Revision 1.27 2004/01/14 11:33:00 reinelt
* new plugin 'uname' which does what it's called
* text widget nearly finished
* first results displayed on MatrixOrbital
*
* Revision 1.26 2004/01/11 18:26:02 reinelt
* further widget and layout processing
*
* Revision 1.25 2004/01/11 09:26:15 reinelt
* layout starts to exist...
*
* Revision 1.24 2004/01/10 20:22:33 reinelt
* added new function 'cfg_list()' (not finished yet)
* added layout.c (will replace processor.c someday)
* added widget_text.c (will be the first and most important widget)
* modified lcd4linux.c so that old-style configs should work, too
*
* Revision 1.23 2004/01/09 04:16:06 reinelt
* added 'section' argument to cfg_get(), but NULLed it on all calls by now.
*
* Revision 1.22 2004/01/08 06:00:28 reinelt
* allowed '.' in key names
* allowed empty section keys (not only "section anything {", but "anything {")
*
* Revision 1.21 2004/01/08 05:28:12 reinelt
* Luk Claes added to AUTHORS
* cfg: section handling ('{}') added
*
* Revision 1.20 2004/01/07 10:15:41 reinelt
* small glitch in evaluator fixed
* made config table sorted and access with bsearch(),
* which should be much faster
*
* Revision 1.19 2003/12/19 05:35:14 reinelt
* renamed 'client' to 'plugin'
*
* Revision 1.18 2003/10/11 06:01:52 reinelt
*
* renamed expression.{c,h} to client.{c,h}
* added config file client
* new functions 'AddNumericVariable()' and 'AddStringVariable()'
* new parameter '-i' for interactive mode
*
* Revision 1.17 2003/10/05 17:58:50 reinelt
* libtool junk; copyright messages cleaned up
*
* Revision 1.16 2003/09/09 06:54:43 reinelt
* new function 'cfg_number()'
*
* Revision 1.15 2003/08/24 05:17:58 reinelt
* liblcd4linux patch from Patrick Schemitz
*
* Revision 1.14 2003/08/14 03:47:40 reinelt
* remove PID file if driver initialisation fails
*
* Revision 1.13 2003/02/22 07:53:10 reinelt
* cfg_get(key,defval)
*
* Revision 1.12 2001/03/09 12:14:24 reinelt
*
* minor cleanups
*
* Revision 1.11 2001/03/08 15:25:38 ltoetsch
* improved exec
*
* Revision 1.10 2001/03/07 18:10:21 ltoetsch
* added e(x)ec commands
*
* Revision 1.9 2000/08/10 09:44:09 reinelt
*
* new debugging scheme: error(), info(), debug()
* uses syslog if in daemon mode
*
* Revision 1.8 2000/07/31 06:46:35 reinelt
*
* eliminated some compiler warnings with glibc
*
* Revision 1.7 2000/04/15 11:13:54 reinelt
*
* added '-d' (debugging) switch
* added several debugging messages
* removed config entry 'Delay' for HD44780 driver
* delay loop for HD44780 will be calibrated automatically
*
* Revision 1.6 2000/04/03 04:46:38 reinelt
*
* added '-c key=val' option
*
* Revision 1.5 2000/03/28 07:22:15 reinelt
*
* version 0.95 released
* X11 driver up and running
* minor bugs fixed
*
* Revision 1.4 2000/03/26 20:00:44 reinelt
*
* README.Raster added
*
* Revision 1.3 2000/03/26 19:03:52 reinelt
*
* more Pixmap renaming
* quoting of '#' in config file
*
* Revision 1.2 2000/03/10 17:36:02 reinelt
*
* first unstable but running release
*
* Revision 1.1 2000/03/10 11:40:47 reinelt
* *** empty log message ***
*
* Revision 1.3 2000/03/07 11:01:34 reinelt
*
* system.c cleanup
*
* Revision 1.2 2000/03/06 06:04:06 reinelt
*
* minor cleanups
*
*/
/*
* exported functions:
*
* cfg_init (source)
* read configuration from source
* returns 0 if successful
* returns -1 in case of an error
*
* cfg_source (void)
* returns the file the configuration was read from
*
* cfg_cmd (arg)
* allows us to overwrite entries in the
* config-file from the command line.
* arg is 'key=value'
* cfg_cmd can be called _before_ cfg_read()
* returns 0 if ok, -1 if arg cannot be parsed
*
* cfg_list (section)
* returns a list of all keys in the specified section
* This list was allocated be cfg_list() and must be
* freed by the caller!
*
* cfg_get_raw (section, key, defval)
* return the a value for a given key in a given section
* or <defval> if key does not exist. Does NOT evaluate
* the expression. Therefore used to get the expression
* itself!
*
* cfg_get (section, key, defval)
* return the a value for a given key in a given section
* or <defval> if key does not exist. The specified
* value in the config is treated as a expression and
* is evaluated!
*
* cfg_number (section, key, defval, min, int max, *value)
* return the a value for a given key in a given section
* convert it into a number with syntax checking
* check if its in a given range. As it uses cfg_get()
* internally, the evaluator is used here, too.
*
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include "debug.h"
#include "evaluator.h"
#include "cfg.h"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif
typedef struct {
char *key;
char *val;
int lock;
} ENTRY;
static char *Config_File=NULL;
static ENTRY *Config=NULL;
static int nConfig=0;
// bsearch compare function for config entries
static int c_lookup (const void *a, const void *b)
{
char *key=(char*)a;
ENTRY *entry=(ENTRY*)b;
return strcasecmp(key, entry->key);
}
// qsort compare function for variables
static int c_sort (const void *a, const void *b)
{
ENTRY *ea=(ENTRY*)a;
ENTRY *eb=(ENTRY*)b;
return strcasecmp(ea->key, eb->key);
}
// remove leading and trailing whitespace
static char *strip (char *s, int strip_comments)
{
char *p;
while (isblank(*s)) s++;
for (p=s; *p; p++) {
if (*p=='"') do p++; while (*p && *p!='\n' && *p!='"');
if (*p=='\'') do p++; while (*p && *p!='\n' && *p!='\'');
if (*p=='\n' || (strip_comments && *p=='#' && (p==s || *(p-1)!='\\'))) {
*p='\0';
break;
}
}
for (p--; p>s && isblank(*p); p--) *p='\0';
return s;
}
// unquote a string
static char *dequote (char *string)
{
int quote=0;
char *s = string;
char *p = string;
do {
if (*s == '\'') {
quote = !quote;
*p++ = *s;
}
else if (quote && *s == '\\') {
s++;
if (*s >= '0' && *s <= '7') {
int n;
unsigned int c = 0;
sscanf (s, "%3o%n", &c, &n);
if (c == 0 || c > 255) {
error ("WARNING: illegal '\\' in <%s>", string);
} else {
*p++ = c;
s += n-1;
}
} else {
*p++ = *s;
}
}
else {
*p++ = *s;
}
} while (*s++);
return string;
}
// which if a string contains only valid chars
// i.e. start with a char and contains chars and nums
static int validchars (char *string)
{
char *c;
for (c=string; *c; c++) {
// first and following chars
if ((*c>='A' && *c<='Z') || (*c>='a' && *c<='z') || (*c=='_')) continue;
// only following chars
if ((c>string) && ((*c>='0' && *c<='9') || (*c=='.') || (*c=='-'))) continue;
return 0;
}
return 1;
}
static void cfg_add (char *section, char *key, char *val, int lock)
{
char *buffer;
ENTRY *entry;
// allocate buffer
buffer=malloc(strlen(section)+strlen(key)+2);
*buffer='\0';
// prepare section.key
if (section!=NULL && *section!='\0') {
strcpy(buffer, section);
strcat(buffer, ".");
}
strcat (buffer, key);
// does the key already exist?
entry=bsearch(buffer, Config, nConfig, sizeof(ENTRY), c_lookup);
if (entry!=NULL) {
if (entry->lock>lock) return;
debug ("Warning: key <%s>: value <%s> overwritten with <%s>", buffer, entry->val, val);
free (buffer);
if (entry->val) free (entry->val);
entry->val=dequote(strdup(val));
return;
}
nConfig++;
Config=realloc(Config, nConfig*sizeof(ENTRY));
Config[nConfig-1].key=buffer;
Config[nConfig-1].val=dequote(strdup(val));
Config[nConfig-1].lock=lock;
qsort(Config, nConfig, sizeof(ENTRY), c_sort);
}
int l4l_cfg_cmd (char *arg)
{
char *key, *val;
char buffer[256];
strncpy (buffer, arg, sizeof(buffer));
key=strip(buffer, 0);
for (val=key; *val; val++) {
if (*val=='=') {
*val++='\0';
break;
}
}
if (*key=='\0' || *val=='\0') return -1;
if (!validchars(key)) return -1;
cfg_add ("", key, val, 1);
return 0;
}
char *l4l_cfg_list (char *section)
{
int i, len;
char *key, *list;
// calculate key length
len=strlen(section)+1;
// prepare search key
key=malloc(len+1);
strcpy (key, section);
strcat (key, ".");
// start with empty string
list=malloc(1);
*list='\0';
// search matching entries
for (i=0; i<nConfig; i++) {
if (strncasecmp(Config[i].key, key, len)==0) {
list=realloc(list, strlen(list)+strlen(Config[i].key)-len+2);
if (*list!='\0') strcat (list, "|");
strcat (list, Config[i].key+len);
}
}
free (key);
return list;
}
static char *cfg_lookup (char *section, char *key)
{
int len;
char *buffer;
ENTRY *entry;
// calculate key length
len=strlen(key)+1;
if (section!=NULL)
len+=strlen(section)+1;
// allocate buffer
buffer=malloc(len);
*buffer='\0';
// prepare section:key
if (section!=NULL && *section!='\0') {
strcpy(buffer, section);
strcat(buffer, ".");
}
strcat (buffer, key);
// search entry
entry=bsearch(buffer, Config, nConfig, sizeof(ENTRY), c_lookup);
// free buffer again
free (buffer);
if (entry!=NULL)
return entry->val;
return NULL;
}
char *l4l_cfg_get_raw (char *section, char *key, char *defval)
{
char *val=cfg_lookup(section, key);
if (val!=NULL) return val;
return defval;
}
char *l4l_cfg_get (char *section, char *key, char *defval)
{
char *expression;
char *retval;
void *tree = NULL;
RESULT result = {0, 0, 0, NULL};
expression=cfg_lookup(section, key);
if (expression!=NULL) {
if (*expression=='\0') return "";
if (Compile(expression, &tree)==0 && Eval(tree, &result)==0) {
retval=strdup(R2S(&result));
DelTree(tree);
DelResult(&result);
return(retval);
}
DelTree(tree);
DelResult(&result);
}
if (defval) return strdup(defval);
return NULL;
}
int l4l_cfg_number (char *section, char *key, int defval, int min, int max, int *value)
{
char *expression;
void *tree = NULL;
RESULT result = {0, 0, 0, NULL};
// start with default value
// in case of an (uncatched) error, you have the
// default value set, which may be handy...
*value=defval;
expression=cfg_get_raw(section, key, NULL);
if (expression==NULL || *expression=='\0') {
return 0;
}
if (Compile(expression, &tree) != 0) {
DelTree(tree);
return -1;
}
if (Eval(tree, &result) != 0) {
DelTree(tree);
DelResult(&result);
return -1;
}
*value=R2N(&result);
DelTree (tree);
DelResult(&result);
if (*value<min) {
error ("bad %s value '%d' in %s, minimum is %d", key, *value, cfg_source(), min);
*value=min;
return -1;
}
if (*value>max) {
error ("bad %s value '%d' in %s, maximum is %d", key, *value, cfg_source(), max);
*value=max;
return -1;
}
return 1;
}
static int cfg_check_source(char *file)
{
/* as passwords and commands are stored in the config file,
* we will check that:
* - file is a normal file (or /dev/null)
* - file owner is owner of program
* - file is not accessible by group
* - file is not accessible by other
*/
struct stat stbuf;
uid_t uid, gid;
int error;
uid = geteuid();
gid = getegid();
if (stat(file, &stbuf) == -1) {
error ("stat(%s) failed: %s", file, strerror(errno));
return -1;
}
if (S_ISCHR(stbuf.st_mode) && strcmp(file, "/dev/null") == 0)
return 0;
error=0;
if (!S_ISREG(stbuf.st_mode)) {
error ("security error: '%s' is not a regular file", file);
error=-1;
}
if (stbuf.st_uid != uid || stbuf.st_gid != gid) {
error ("security error: owner and/or group of '%s' don't match", file);
error=-1;
}
if (stbuf.st_mode & S_IRWXG || stbuf.st_mode & S_IRWXO) {
error ("security error: group or other have access to '%s'", file);
error=-1;
}
return error;
}
static int cfg_read (char *file)
{
FILE *stream;
char buffer[256];
char section[256];
char *line, *key, *val, *end;
int section_open, section_close;
int error, lineno;
stream=fopen (file, "r");
if (stream==NULL) {
error ("open(%s) failed: %s", file, strerror(errno));
return -1;
}
// start with empty section
strcpy(section, "");
error=0;
lineno=0;
while ((line=fgets(buffer,256,stream))!=NULL) {
// increment line number
lineno++;
// skip empty lines
if (*(line=strip(line, 1))=='\0') continue;
// reset section flags
section_open=0;
section_close=0;
// key is first word
key=line;
// search first blank between key and value
for (val=line; *val; val++) {
if (isblank(*val)) {
*val++='\0';
break;
}
}
// strip value
val=strip(val, 1);
// search end of value
if (*val) for (end=val; *(end+1); end++);
else end=val;
// if last char is '{', a section has been opened
if (*end=='{') {
section_open=1;
*end='\0';
val=strip(val, 0);
}
// provess "value" in double-quotes
if (*val=='"' && *end=='"') {
*end='\0';
val++;
}
// if key is '}', a section has been closed
if (strcmp(key, "}")==0) {
section_close=1;
*key='\0';
}
// sanity check: '}' should be the only char in a line
if (section_close && (section_open || *val!='\0')) {
error ("error in config file '%s' line %d: garbage after '}'", file, lineno);
error=1;
break;
}
// check key for valid chars
if (!validchars(key)) {
error ("error in config file '%s' line %d: key '%s' is invalid", file, lineno, key);
error=1;
break;
}
// on section-open, check value for valid chars
if (section_open && !validchars(val)) {
error ("error in config file '%s' line %d: section '%s' is invalid", file, lineno, val);
error=1;
break;
}
// on section-open, append new section name
if (section_open) {
// is the section[] array big enough?
if (strlen(section)+strlen(key)+3 > sizeof(section)) {
error ("error in config file '%s' line %d: section buffer overflow", file, lineno);
error=1;
break;
}
if (*section!='\0') strcat (section, ".");
strcat (section, key);
if (*val!='\0') {
strcat (section, ":");
strcat (section, val);
}
continue;
}
// on section-close, remove last section name
if (section_close) {
// sanity check: section already empty?
if (*section=='\0') {
error ("error in config file '%s' line %d: unmatched closing brace", file, lineno);
error=1;
break;
}
end=strrchr(section, '.');
if (end==NULL)
*section='\0';
else
*end='\0';
continue;
}
// finally: add key
cfg_add (section, key, val, 0);
}
// sanity check: are the braces balanced?
if (!error && *section!='\0') {
error ("error in config file '%s' line %d: unbalanced braces", file, lineno);
error=1;
}
fclose (stream);
return -error;
}
int l4l_cfg_init (char *file)
{
if (cfg_check_source(file) == -1) {
error("config file '%s' is insecure, aborting", file);
return -1;
}
if (cfg_read(file)<0) return -1;
if (Config_File) free (Config_File);
Config_File=strdup(file);
return 0;
}
char *l4l_cfg_source (void)
{
if (Config_File)
return Config_File;
else
return "";
}
int l4l_cfg_exit (void)
{
int i;
for (i=0; i<nConfig; i++) {
if (Config[i].key) free (Config[i].key);
if (Config[i].val) free (Config[i].val);
}
if (Config) {
free (Config);
Config=NULL;
}
if (Config_File) {
free (Config_File);
Config_File=NULL;
}
return 0;
}
int (*cfg_init) (char *source) = l4l_cfg_init;
char *(*cfg_source) (void) = l4l_cfg_source;
int (*cfg_cmd) (char *arg) = l4l_cfg_cmd;
char *(*cfg_list) (char *section) = l4l_cfg_list;
char *(*cfg_get_raw) (char *section, char *key, char *defval) = l4l_cfg_get_raw;
char *(*cfg_get) (char *section, char *key, char *defval) = l4l_cfg_get;
int (*cfg_number) (char *section, char *key, int defval,
int min, int max, int *value) = l4l_cfg_number;
int (*cfg_exit) (void) = l4l_cfg_exit;