guowenxue
2021-04-20 c7b6f4f68a6c9e8e83849601bd0f5240c47c317f
Add iotd program framework source code
30 files added
5786 ■■■■■ changed files
iotd/3rdlib/build.sh 179 ●●●●● patch | view | raw | blame | history
iotd/3rdlib/makefile 7 ●●●●● patch | view | raw | blame | history
iotd/conf/conf.c 229 ●●●●● patch | view | raw | blame | history
iotd/conf/conf.h 67 ●●●●● patch | view | raw | blame | history
iotd/conf/makefile 15 ●●●●● patch | view | raw | blame | history
iotd/etc/iotd.conf 80 ●●●●● patch | view | raw | blame | history
iotd/hal/ds18b20.c 107 ●●●●● patch | view | raw | blame | history
iotd/hal/ds18b20.h 19 ●●●●● patch | view | raw | blame | history
iotd/hal/gpio.c 97 ●●●●● patch | view | raw | blame | history
iotd/hal/gpio.h 58 ●●●●● patch | view | raw | blame | history
iotd/hal/hal.c 79 ●●●●● patch | view | raw | blame | history
iotd/hal/hal.h 42 ●●●●● patch | view | raw | blame | history
iotd/hal/makefile 15 ●●●●● patch | view | raw | blame | history
iotd/hal/sht20.c 464 ●●●●● patch | view | raw | blame | history
iotd/hal/sht20.h 33 ●●●●● patch | view | raw | blame | history
iotd/hal/tsl2561.c 199 ●●●●● patch | view | raw | blame | history
iotd/hal/tsl2561.h 42 ●●●●● patch | view | raw | blame | history
iotd/lylib/ini_dictionary.c 398 ●●●●● patch | view | raw | blame | history
iotd/lylib/ini_dictionary.h 165 ●●●●● patch | view | raw | blame | history
iotd/lylib/ini_parser.c 807 ●●●●● patch | view | raw | blame | history
iotd/lylib/ini_parser.h 308 ●●●●● patch | view | raw | blame | history
iotd/lylib/linux_list.h 723 ●●●●● patch | view | raw | blame | history
iotd/lylib/logger.c 416 ●●●●● patch | view | raw | blame | history
iotd/lylib/logger.h 113 ●●●●● patch | view | raw | blame | history
iotd/lylib/makefile 15 ●●●●● patch | view | raw | blame | history
iotd/lylib/util_proc.c 376 ●●●●● patch | view | raw | blame | history
iotd/lylib/util_proc.h 64 ●●●●● patch | view | raw | blame | history
iotd/lylib/util_time.h 185 ●●●●● patch | view | raw | blame | history
iotd/main.c 410 ●●●●● patch | view | raw | blame | history
iotd/makefile 74 ●●●●● patch | view | raw | blame | history
iotd/3rdlib/build.sh
New file
@@ -0,0 +1,179 @@
#!/bin/bash
CROSSTOOL=
INST_PATH=`pwd`/install
LYFTP_SRC=ftp://master.iot-yun.club/src/
export CFLAGS=
export LDFLAGS=
function msg_banner()
{
    echo ""
    echo "+-----------------------------------------------------------------------"
    echo "|  $1 "
    echo "+-----------------------------------------------------------------------"
    echo ""
}
function check_result()
{
           if [ $? != 0 ] ; then
               echo ""
               echo "+-----------------------------------------------------------------------"
               echo "|  $1 "
               echo "+-----------------------------------------------------------------------"
               echo ""
               exit ;
           fi
}
function export_cross()
{
    msg_banner "export cross compiler tools"
    # export cross toolchain
    export CC=${CROSSTOOL}gcc
    export AS=${CROSSTOOL}as
    export AR=${CROSSTOOL}ar
    export LD=${CROSSTOOL}ld
    export NM=${CROSSTOOL}nm
    export RANLIB=${CROSSTOOL}ranlib
    export OBJDUMP=${CROSSTOOL}objdump
    export STRIP=${CROSSTOOL}strip
    # export cross configure
    export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux "
    # Clear LDFLAGS and CFLAGS
    export LDFLAGS=
    export CFLAGS=
}
function compile_libgpiod()
{
    PREFIX_PATH=${INST_PATH}
    SRC_NAME=libgpiod-1.6.2
    PACK_SUFIX=tar.gz
    IMG_NAME=libgpiod.so
    if [ -f ${PREFIX_PATH}/lib/${IMG_NAME} ] ; then
        msg_banner "$SRC_NAME already compile and installed"
        return 0;
    fi
    msg_banner "Start compile $SRC_NAME "
    if [ ! -f ${SRC_NAME}.${PACK_SUFIX} ] ; then
        #wget https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/$SRC_NAME.$PACK_SUFIX
        wget $LYFTP_SRC/$SRC_NAME.$PACK_SUFIX
        check_result "ERROR: download ${SRC_NAME} failure"
    fi
    tar -xzf ${SRC_NAME}.${PACK_SUFIX}
    cd ${SRC_NAME}
    ./autogen.sh
    echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache
    ./configure --enable-tools=yes --prefix=${PREFIX_PATH} ${CONFIG_CROSS} --cache-file=arm-linux.cache
    make && make install
    cd -
}
function compile_cJson()
{
    PREFIX_PATH=${INST_PATH}
    SRC_NAME=cJSON-1.7.14
    PACK_SUFIX=tar.gz
    IMG_NAME=libcjson.so
    if [ -f ${PREFIX_PATH}/lib/${IMG_NAME} ] ; then
        msg_banner "$SRC_NAME already compile and installed"
        return 0;
    fi
    msg_banner "Start compile $SRC_NAME "
    if [ ! -f ${SRC_NAME}.${PACK_SUFIX} ] ; then
        # official site: https://github.com/DaveGamble/cJSON
        wget $LYFTP_SRC/$SRC_NAME.$PACK_SUFIX
        check_result "ERROR: download ${SRC_NAME} failure"
    fi
    tar -xzf ${SRC_NAME}.${PACK_SUFIX}
    cd ${SRC_NAME}
    sed -i -e "s|^CC =.*|CC = ${CC} -std=c89|g" Makefile
    make && make PREFIX=$PREFIX_PATH install
    cd -
}
function compile_mosquitto()
{
    PREFIX_PATH=${INST_PATH}
    SRC_NAME=mosquitto-2.0.5
    PACK_SUFIX=tar.gz
    IMG_NAME=libmosquitto.so
    if [ -f ${PREFIX_PATH}/lib/${IMG_NAME} ] ; then
        msg_banner "$SRC_NAME already compile and installed"
        return 0;
    fi
    # mosquitto depends on openssl
    if [ ! -n "${CROSSTOOL}" ] ; then
       msg_banner "Using apt install openssl library now..."
       sudo apt install -y libssl-dev
    fi
    msg_banner "Start compile $SRC_NAME "
    if [ ! -f ${SRC_NAME}.${PACK_SUFIX} ] ; then
        wget $LYFTP_SRC/$SRC_NAME.$PACK_SUFIX
        #wget https://mosquitto.org/files/source/$SRC_NAME.$PACK_SUFIX
        check_result "ERROR: download ${SRC_NAME} failure"
    fi
    tar -xzf ${SRC_NAME}.${PACK_SUFIX}
    cd ${SRC_NAME}
    export CFLAGS=-I${PREFIX_PATH}/include
    export LDFLAGS=-L${PREFIX_PATH}/lib
    make  LDFLAGS=${LDFLAGS} && make DESTDIR=${PREFIX_PATH} prefix=/ install
    cd -
}
if [[ $# == 1 ]] && [[ $1 == clean ]] ; then
    rm -rf install
    rm -rf libgpiod-1.6.2
    rm -rf cJSON-1.7.14
    rm -rf mosquitto-2.0.5
    exit 0;
fi
if [ -n "${CROSSTOOL}" ] ; then
    export_cross
fi
compile_libgpiod
compile_cJson
compile_mosquitto
iotd/3rdlib/makefile
New file
@@ -0,0 +1,7 @@
all:
    sed -i -e "s|^CROSSTOOL=.*|CROSSTOOL=${CROSSTOOL}|g" build.sh
    @bash build.sh
distclean:
    @bash build.sh clean
iotd/conf/conf.c
New file
@@ -0,0 +1,229 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  conf.c
 *    Description:  This file is iotd configure file parser function
 *
 *        Version:  1.0.0(2019年06月25日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
 *
 ********************************************************************************/
#include "conf.h"
#include "lylib/logger.h"
#include "lylib/ini_parser.h"
enum
{
    _TYPE_INPUT,
    _TYPE_OUTPUT,
};
int parser_gpio_info(int type, gpio_t *gpio, char *conf)
{
    char              *pstart;
    char              *pend;
    if( !gpio || !conf || strlen(conf)<3 )
    {
        log_err("Invalid input arguments.\n");
        return -1;
    }
    printf("conf: %s\n", conf);
    pstart = strchr(conf, '{');
    pend = strchr(conf, '}');
}
int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug)
{
    dictionary          *ini;
    char                *str;
    int                  val;
    log_ctx_t           *log_ctx;
    hal_ctx_t           *hal_ctx;
    mqtt_ctx_t          *mqtt_ctx;
    gpio_t              *gpio;
    static logger_t      logger;
    if( !conf_file || !ctx )
    {
        fprintf(stderr, "ERROR: parser configure file or ctx is NULL\n");
        return 0;
    }
    memset(ctx, 0, sizeof(*ctx));
    log_ctx = &ctx->log_ctx;
    hal_ctx = &ctx->hal_ctx;
    mqtt_ctx = &ctx->mqtt_ctx;
    ini = iniparser_load(conf_file);
    if( !ini )
    {
        fprintf(stderr, "ERROR: cannot parse file: '%s'\n", conf_file);
        return -1;
    }
    /*+------------------------------------------------------+
     *|    parser logger settings and start logger system    |
     *+------------------------------------------------------+*/
    if( !debug )
    {
        str = iniparser_getstring(ini, "logger:file", "/tmp/iotd.log");
        strncpy(log_ctx->logfile, str, sizeof(log_ctx->logfile));
        log_ctx->logsize = iniparser_getint(ini, "logger:size", 1024);
        log_ctx->loglevel = iniparser_getint(ini, "logger:level", LOG_LEVEL_DEBUG);
    }
    else
    {
        strncpy(log_ctx->logfile, DBG_LOG_FILE, sizeof(log_ctx->logfile));
        log_ctx->loglevel = LOG_LEVEL_DEBUG;
    }
    if( logger_init(&logger, log_ctx->logfile, log_ctx->loglevel, log_ctx->logsize) < 0 || logger_open()<0 )
    {
        fprintf(stderr, "Logger system initialise failure\n");
        return -2;
    }
    log_nrml("Logger system initialise ok\n");
    /*+------------------------------------------------------+
     *|               parser production ID                   |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "common:id", NULL)) )
    {
        log_err("ERROR: parser production ID failure\n");
        return -2;
    }
    /* cJSON parser ID will get ""  */
    snprintf(mqtt_ctx->id, sizeof(mqtt_ctx->id), "\"%s\"", str);
    log_nrml("parser production ID [%s]\n", mqtt_ctx->id);
    /*+------------------------------------------------------+
     *|      parser hardware module configuration ID         |
     *+------------------------------------------------------+*/
    gpio = &hal_ctx->gpio;
    /* parser GPIO output pins  */
    if( !(str=iniparser_getstring(ini, "hardware:gpio_outpin", NULL)) )
    {
        log_warn("parser no GPIO output pins\n");
    }
    else
    {
        parser_gpio_info(_TYPE_OUTPUT, gpio, str);
        log_nrml("parser [%d] GPIO output pins configured\n", gpio->outcnt);
    }
    gpio->light_intval = iniparser_getint(ini, "hardware:light_intval", 20);
    log_nrml("parser relay controled light interval time [%d]\n", gpio->light_intval);
    /* parser GPIO input pins  */
    if( !(str=iniparser_getstring(ini, "hardware:gpio_inpin", NULL)) )
    {
        log_warn("parser no GPIO input pins\n");
    }
    else
    {
        parser_gpio_info(_TYPE_INPUT, gpio, str);
        log_nrml("parser [%d] GPIO input pins\n", gpio->incnt);
    }
    hal_ctx->lux_enable = iniparser_getint(ini, "hardware:lux", 0);
    if( hal_ctx->lux_enable )
    {
        hal_ctx->lux_threshold = iniparser_getdouble(ini, "hardware:lux_threshold", 0.1);
        log_nrml("parser LUX enable and threshold value set be [%.03f]\n", hal_ctx->lux_threshold);
    }
    hal_ctx->sht2x_enable = iniparser_getint(ini, "hardware:sht2x", 0);
    if( hal_ctx->sht2x_enable )
    {
        log_nrml("parser SHT2x sensor enabled\n");
    }
    hal_ctx->ds18b20_enable = iniparser_getint(ini, "hardware:sht2x", 0);
    if( hal_ctx->ds18b20_enable )
    {
        log_nrml("parser DS18B20 sensor enabled\n");
    }
    /*+------------------------------------------------------+
     *|              parser broker settings                  |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "broker:hostname", NULL)) )
    {
        log_err("ERROR: Parser broker server hostname failure\n");
        return -2;
    }
    strncpy(mqtt_ctx->host, str, sizeof(mqtt_ctx->host) );
    if( (val=iniparser_getint(ini, "broker:port", -1)) < 0 )
    {
        log_err("ERROR: Parser broker server port failure\n");
        return -2;
    }
    mqtt_ctx->port = val;
    log_nrml("Parser broker server [%s:%d]\n", mqtt_ctx->host, mqtt_ctx->port);
    str=iniparser_getstring(ini, "broker:username", NULL);
    strncpy(mqtt_ctx->uid, str, sizeof(mqtt_ctx->uid) );
    str=iniparser_getstring(ini, "broker:password", NULL);
    strncpy(mqtt_ctx->pwd, str, sizeof(mqtt_ctx->pwd) );
    log_nrml("Parser broker author by [%s:%s]\n", mqtt_ctx->uid, mqtt_ctx->pwd);
    mqtt_ctx->keepalive = iniparser_getint(ini, "broker:keepalive", 30);
    log_nrml("Parser broker keepalive timeout [%d] seconds\n", mqtt_ctx->keepalive);
    /*+------------------------------------------------------+
     *|             parser subscriber settings               |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "subsciber:subTopic", NULL)) )
    {
        log_err("ERROR: Parser MQTT subscribe topic failure\n");
        return -2;
    }
    strncpy(mqtt_ctx->subTopic, str, sizeof(mqtt_ctx->subTopic) );
    mqtt_ctx->subQos = iniparser_getint(ini, "subsciber:subQos", 0);
    log_nrml("Parser subscriber topic \"%s\" with Qos[%d]\n", mqtt_ctx->subTopic, mqtt_ctx->subQos);
    /*+------------------------------------------------------+
     *|             parser publisher settings                |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "publisher:pubTopic", NULL)) )
    {
        log_err("ERROR: Parser MQTT publisher topic failure\n");
        return -2;
    }
    strncpy(mqtt_ctx->pubTopic, str, sizeof(mqtt_ctx->pubTopic) );
    mqtt_ctx->pubQos = iniparser_getint(ini, "publisher:pubQos", 0);
    log_nrml("Parser publisher topic \"%s\" with Qos[%d]\n", mqtt_ctx->pubTopic, mqtt_ctx->pubQos);
    return 0;
}
iotd/conf/conf.h
New file
@@ -0,0 +1,67 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  conf.h
 *    Description:  This file is iotd configure file parser function
 *
 *        Version:  1.0.0(2019年06月25日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
 *
 ********************************************************************************/
#ifndef  __CONF_H_
#define  __CONF_H_
#include "hal/hal.h"
enum
{
    Qos0, /* 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息 */
    Qos1, /* 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息 */
    Qos2, /* Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次  */
};
typedef struct log_ctx_s
{
    /* logger settings */
    char          logfile[128]; /* logger record file */
    int           loglevel;     /* logger level  */
    int           logsize;      /* logger file maxsize, oversize will rollback */
} log_ctx_t;
typedef struct mqtt_ctx_s
{
    char          id[32];       /*  production ID */
    /* Broker settings  */
    char          host[128];  /* MQTT broker server name  */
    int           port;       /* MQTT broker listen port  */
    char          uid[64];    /* username */
    char          pwd[64];    /* password */
    int           keepalive;  /* MQTT broker send PING message to subsciber/publisher keepalive timeout<seconds> */
    /* Subscriber settings */
    char          subTopic[256]; /* Subscriber topic */
    int           subQos;        /* Subscriber Qos  */
    /* Publisher settings */
    char          pubTopic[256]; /* Publisher topic */
    int           pubQos;        /* Publisher Qos   */
    int           interval;      /* publish interval */
} mqtt_ctx_t;
typedef struct iotd_ctx_s
{
    log_ctx_t     log_ctx;
    hal_ctx_t     hal_ctx;
    mqtt_ctx_t    mqtt_ctx;
} iotd_ctx_t;
extern int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug);
#endif   /* ----- #ifndef _CONF_H_  ----- */
iotd/conf/makefile
New file
@@ -0,0 +1,15 @@
PWD=$(shell pwd )
LIBNAME=$(shell basename ${PWD} )
TOPDIR=$(shell dirname ${PWD} )
all: clean
    @rm -f *.o
    ${CROSSTOOL}gcc ${CFLAGS} -I${TOPDIR} -c *.c
    ${CROSSTOOL}ar -rcs  lib${LIBNAME}.a *.o
clean:
    @rm -f *.o
    @rm -f *.a
iotd/etc/iotd.conf
New file
@@ -0,0 +1,80 @@
[common]
id="RPI3B#01"
[logger]
# 日志记录文件
file=/tmp/iotd.log
# 日志级别: 0:Disable 1:Fatal 2:ERROR 3:warnning 4:Normal 5:Debug 6:Infor 7:Trace
level=4
# 日志回滚大小
size=1024
#+-------------------------------------------+
#|         Hardware configuration            |
#+-------------------------------------------+
# 树莓派连接的外设信息,0:禁用或未连接  其他: 使能或相关硬件连接的Pin管脚(BCM)
[hardware]
# LED或继电器等GPIO输出控制, 格式: {名称:BCM编码:控制:电平}
gpio_outpin={light_indoor:6:0},{light_livroomL:13:0},{light_livroomR:19:0},{light_hallway:26:0}
#gpio_outpin={led_red:6:1},{led_green:13:1},{led_blue:19:1}
# 红外探测到人后,继电器控制灯亮的时长
light_intval=15
# 按键或红外感应灯GPIO输入控制, 格式: {名称:BCM编码:控制:电平}
gpio_inpin={infrared_indoor:4:0},{infrared_hallway:18:0}
# 是否使能 TSL2561 光强传感器模块,0:禁用  X:光强阈值
lux=1
lux_threshould=0.10
# 是否使能 DS18b20 温度传感器模块,0:禁用  1:是能
# ds18b20=1
# 是否使能 SHT2X 温湿度传感器模块,0:禁用  1:使能
# sht2x=0
#+-------------------------------------------+
#|           MQTT configuration              |
#+-------------------------------------------+
[broker]
# broker 服务器地址和端口号
hostname="master.iot-yun.club"
port=10883
# broker 认证连接的用户名和密码
username="lingyun"
password="lingyun-emb"
# broker给subsciber和publisher发送PING报文保持 keepalive 的时间周期,单位是秒
keepalive=30
# Qos0: 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息
# Qos1: 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息
# Qos2: Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次
[subsciber]
subTopic="$Sys/Studio/iotd/Downlink"
subQos=0
[publisher]
pubTopic="$Sys/Studio/iotd/Uplink"
pubQos=0
# Publisher上报传感器数据的周期,单位是秒
interval=60
iotd/hal/ds18b20.c
New file
@@ -0,0 +1,107 @@
/*********************************************************************************
 *      Copyright:  (C) 2018 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  ds18b20.c
 *    Description:  This file is temperature sensor DS18B20 code
 *
 *        Version:  1.0.0(2018/10/14)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "lylib/logger.h"
/* File Content:
   pi@raspberrypi:~/guowenxue $ cat /sys/bus/w1/devices/28-041731f7c0ff/w1_slave
   3a 01 4b 46 7f ff 0c 10 a5 : crc=a5 YES
   3a 01 4b 46 7f ff 0c 10 a5 t=19625
 */
int ds18b20_get_temperature(float *temp)
{
    char            w1_path[50] = "/sys/bus/w1/devices/";
    char            chip[20];
    char            buf[128];
    DIR            *dirp;
    struct dirent  *direntp;
    int             fd =-1;
    char           *ptr;
    float           value;
    int             found = 0;
    if( !temp )
    {
            return -1;
    }
    /*+-------------------------------------------------------------------+
     *|  open dierectory /sys/bus/w1/devices to get chipset Serial Number |
     *+-------------------------------------------------------------------+*/
    if((dirp = opendir(w1_path)) == NULL)
    {
        log_err("opendir '%s' error: %s\n", w1_path, strerror(errno));
        return -2;
    }
    while((direntp = readdir(dirp)) != NULL)
    {
        if(strstr(direntp->d_name,"28-"))
        {
            /* find and get the chipset SN filename */
            strcpy(chip,direntp->d_name);
            found = 1;
        break;
        }
    }
    closedir(dirp);
    if( !found )
    {
            log_fatal("Can not find ds18b20 in %s\n", w1_path);
            return -3;
    }
    /* get DS18B20 sample file full path: /sys/bus/w1/devices/28-xxxx/w1_slave */
    strncat(w1_path, chip, sizeof(w1_path)-strlen(w1_path));
    strncat(w1_path, "/w1_slave", sizeof(w1_path)-strlen(w1_path));
    /* open file /sys/bus/w1/devices/28-xxxx/w1_slave to get temperature */
    if( (fd=open(w1_path, O_RDONLY)) < 0 )
    {
            log_err("open %s error: %s\n", w1_path, strerror(errno));
            return -4;
    }
    if(read(fd, buf, sizeof(buf)) < 0)
    {
            log_err("read %s error: %s\n", w1_path, strerror(errno));
            return -5;
    }
    ptr = strstr(buf, "t=");
    if( !ptr )
    {
            log_err("ERROR: Can not get temperature\n");
            return -6;
    }
    ptr+=2;
    /* convert string value to float value */
    *temp = atof(ptr)/1000;
    close(fd);
    return 0;
}
iotd/hal/ds18b20.h
New file
@@ -0,0 +1,19 @@
/*********************************************************************************
 *      Copyright:  (C) 2018 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  ds18b20.c
 *    Description:  This file is temperature sensor DS18B20 code
 *
 *        Version:  1.0.0(2018/10/14)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
 *
 ********************************************************************************/
#ifndef __DS18B20_H
#define __DS18B20_H
extern int ds18b20_get_temperature(float *temp);
#endif
iotd/hal/gpio.c
New file
@@ -0,0 +1,97 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  gpio.c
 *    Description:  This file is GPIO input/output functions
 *
 *        Version:  1.0.0(2019年06月24日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
 *
 ********************************************************************************/
#include "lylib/logger.h"
#include "gpio.h"
#define RPI_GPIONAME        "gpiochip0"
static struct gpiod_chip    *s_chip;
static gpio_t               *s_gpio = NULL;
int gpio_init(gpio_t *gpio)
{
    if( !gpio )
    {
        log_err("Invalid input arguments $gpio\n");
        return -1;
    }
    s_gpio = gpio;
}
void gpio_term(void)
{
    return ;
}
void gpio_out(char *name, int cmd)
{
    int              i;
    int              found = 0;
    for( i=0; i<s_gpio->outcnt; i++ )
    {
        if( !strncasecmp(s_gpio->output[i].name, name, strlen(name)))
        {
            found = 1;
            break;
        }
    }
    if( !found )
    {
        log_err("GPIO output for [%s] pin not found\n", name);
        return ;
    }
    if( OFF == cmd )
    {
        gpiod_line_set_value(s_gpio->output[i].lines, s_gpio->output[i].active_level);
    }
    else
    {
        gpiod_line_set_value(s_gpio->output[i].lines, !s_gpio->output[i].active_level);
    }
    return ;
}
#if 0
/* Return value: 1(HIGH): Sombody detected  0(LOW): Nobody detected */
int infrared_detect(void)
{
    struct gpiod_line_event  event;
    /* This function will block, must call it to setup */
    if( gpiod_line_event_wait(s_infrared_lines, NULL) < 0 )
    {
        //log_err("infrared gpiod line wait event failure!\n");
        return 0;
    }
    /* This function will block, must read to clear the event  */
    if (gpiod_line_event_read(s_infrared_lines, &event) < 0)
    {
        log_err("gpiod line event read failure!\n");
        return 0;
    }
    if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
        return 1;
    else
        return 0;
}
#endif
iotd/hal/gpio.h
New file
@@ -0,0 +1,58 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  gpio.h
 *    Description:  This file is GPIO input/output functions
 *
 *        Version:  1.0.0(2019年06月24日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
 *
 ********************************************************************************/
#ifndef  _GPIO_H_
#define  _GPIO_H_
#include <gpiod.h>
#define OFF   0
#define ON    1
#define GPIO_MAXOUT         8
#define GPIO_MAXIN          4
typedef struct gpio_info_s
{
    char                 name[32];      /*  GPIO connected module name */
    int                  pins;          /*  GPIO BCM pin number */
    int                  active_level;  /*  active power level */
    struct gpiod_line   *lines;         /*  gpiod lines */
} gpio_info_t;
typedef struct gpio_s
{
    gpio_info_t          output[GPIO_MAXOUT]; /* GPIO output pins */
    int                  outcnt;              /* GPIO output numbers */
    int                  light_intval;        /* light on interval time */
    gpio_info_t          input[GPIO_MAXIN];   /* GPIO input pins */
    int                  incnt;               /* GPIO input numbers */
} gpio_t;
extern int gpio_init(gpio_t *gpio);
extern void gpio_term(void);
/* turn which light on/off */
extern void turn_light(int which, int cmd);
/* turn which led on/off */
extern void turn_led(int which, int cmd);
/* Return value: 0(LOW): Nobody detected, !0: Which infrared detect incoming */
extern int infrared_detect(void);
#endif   /* ----- #ifndef _GPIO_H_  ----- */
iotd/hal/hal.c
New file
@@ -0,0 +1,79 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  hal.c
 *    Description:  This file is RPi HAL(Hardware Abstract Layer) initial functions
 *
 *        Version:  1.0.0(2019年06月24日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
 *
 ********************************************************************************/
#include "lylib/logger.h"
#include "hal.h"
int hal_init(hal_ctx_t *ctx)
{
    int                   i;
    gpio_info_t          *gpio = NULL;
    if(!ctx)
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    if( ctx->sht2x_enable )
    {
        if( sht2x_init()< 0 )
        {
            log_err("R&H sensor SHT2X initialise failure\n");
            return -2;
        }
    }
    if( ctx->lux_enable )
    {
        if( tsl2561_init()< 0 )
        {
            log_err("LUX sensor TSL2561 initialise failure\n");
            return -2;
        }
    }
    gpio_init(&ctx->gpio);
    return 0;
}
void hal_term(hal_ctx_t *ctx)
{
    int             i;
    if(!ctx)
    {
        log_err("Invalid input arguments\n");
        return ;
    }
    if( ctx->sht2x_enable )
    {
        sht2x_term();
    }
    if( ctx->lux_enable )
    {
        tsl2561_term();
    }
    gpio_term();
    return ;
}
iotd/hal/hal.h
New file
@@ -0,0 +1,42 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  hal.h
 *    Description:  This file is RPi HAL(Hardware Abstract Layer) initial functions
 *
 *        Version:  1.0.0(2019年06月24日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
 *
 ********************************************************************************/
#ifndef  _HAL_H_
#define  _HAL_H_
#include "ds18b20.h"
#include "sht20.h"
#include "tsl2561.h"
#include "gpio.h"
typedef struct hal_ctx_s
{
    int                  ds18b20_enable; /* 0:Disable   !0: Enable */
    int                  sht2x_enable;   /* 0:Disable   !0: Enable */
    int                  lux_enable;     /* 0:Disable   !0: Enable */
    float                lux_threshold;  /* Lux Threshold value */
    gpio_t               gpio;           /* gpio intput/output pins  */
} hal_ctx_t;
/* init hardware */
extern int hal_init(hal_ctx_t *ctx);
/* terminal hardware */
extern void hal_term(hal_ctx_t *ctx);
#endif   /* ----- #ifndef _HAL_H_  ----- */
iotd/hal/makefile
New file
@@ -0,0 +1,15 @@
PWD=$(shell pwd )
LIBNAME=$(shell basename ${PWD} )
TOPDIR=$(shell dirname ${PWD} )
all: clean
    @rm -f *.o
    @${CROSSTOOL}gcc ${CFLAGS} -I${TOPDIR} -c *.c
    ${CROSSTOOL}ar -rcs  lib${LIBNAME}.a *.o
clean:
    @rm -f *.o
    @rm -f *.a
iotd/hal/sht20.c
New file
@@ -0,0 +1,464 @@
/*********************************************************************************
 *      Copyright:  (C) 2018 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  sht20.c
 *    Description:  This file is temperature and relative humidity sensor SHT20 code
 *
 *        Version:  1.0.0(2018/10/14)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
 *
 ********************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <sys/stat.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include "sht20.h"
#include "lylib/logger.h"
#include "lylib/util_time.h"
static int s_sht2x_fd = -1;
void sht2x_term(void)
{
    close(s_sht2x_fd);
    s_sht2x_fd = -1;
    log_warn("Terminate SHT2X\n");
}
static inline void dump_buf(const char *prompt, uint8_t *buf, int size)
{
    int          i;
    if( !buf )
    {
        return ;
    }
    if( prompt )
    {
        printf("%s ", prompt);
    }
    for(i=0; i<size; i++)
    {
        printf("%02x ", buf[i]);
    }
    printf("\n");
    return ;
}
#ifdef I2C_API_RDWR  /* Use I2C userspace driver read/write API */
int sht2x_softreset(int fd)
{
    uint8_t           buf[4];
    if( fd<0 )
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    /* software reset SHT2x */
    memset(buf, 0, sizeof(buf));
    buf[0] = SOFTRESET;
    write(fd, buf, 1);
    msleep(50);
    return 0;
}
int sht2x_init(void)
{
    if( (s_sht2x_fd=open("/dev/i2c-1", O_RDWR)) < 0)
    {
        log_fatal("i2c device open failed: %s\n", strerror(errno));
        return -1;
    }
    /* set I2C mode and SHT2x slave address */
    ioctl(s_sht2x_fd, I2C_TENBIT, 0);    /* Not 10-bit but 7-bit mode */
    ioctl(s_sht2x_fd, I2C_SLAVE, 0x40); /* set SHT2x slava address 0x40*/
    if( sht2x_softreset(s_sht2x_fd) < 0 )
    {
        log_err("SHT2x softreset failure\n");
        sht2x_term();
        return -2;
    }
    return s_sht2x_fd;
}
int sht2x_get_temp_humidity(float *temp, float *rh)
{
    uint8_t           buf[4];
    if( !temp || !rh )
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    if( s_sht2x_fd < 0 )
    {
        if( sht2x_init() < 0)
        {
            log_err("SHT2x initialise failure\n");
            return -2;
        }
    }
    /* send trigger temperature measure command and read the data */
    memset(buf, 0, sizeof(buf));
    buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
    write(s_sht2x_fd, buf, 1);
    msleep(85); /* datasheet: typ=66, max=85 */
    memset(buf, 0, sizeof(buf));
    read(s_sht2x_fd, buf, 3);
    //dump_buf("Temperature sample data: ", buf, 3);
    *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
    /* send trigger humidity measure command and read the data */
    memset(buf, 0, sizeof(buf));
    buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
    write(s_sht2x_fd, buf, 1);
    msleep(29); /* datasheet: typ=22, max=29 */
    memset(buf, 0, sizeof(buf));
    read(s_sht2x_fd, buf, 3);
    //dump_buf("Relative humidity sample data: ", buf, 3);
    *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
    return 0;
}
int sht2x_get_serialnumber(uint8_t *serialnumber, int size)
{
    uint8_t           buf[4];
    if( !serialnumber || size!=8 )
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    if( s_sht2x_fd < 0 )
    {
        if( sht2x_init() < 0)
        {
            log_err("SHT2x initialise failure\n");
            return -2;
        }
    }
    /* Read SerialNumber from Location 1 */
    memset(buf, 0, sizeof(buf));
    buf[0] = 0xfa;  /* command for readout on-chip memory */
    buf[1] = 0x0f;  /* on-chip memory address */
    write(s_sht2x_fd, buf, 2);
    memset(buf, 0, sizeof(buf));
    read(s_sht2x_fd, buf, 4);
    serialnumber[5]=buf[0]; /* Read SNB_3 */
    serialnumber[4]=buf[1]; /* Read SNB_2 */
    serialnumber[3]=buf[2]; /* Read SNB_1 */
    serialnumber[2]=buf[3]; /* Read SNB_0 */
    /* Read SerialNumber from Location 2 */
    memset(buf, 0, sizeof(buf) );
    buf[0]=0xfc;  /* command for readout on-chip memory */
    buf[1]=0xc9;  /* on-chip memory address */
    write(s_sht2x_fd, buf, 2);
    memset(buf, 0, sizeof(buf) );
    read(s_sht2x_fd, buf, 4);
    serialnumber[1]=buf[0]; /* Read SNC_1 */
    serialnumber[0]=buf[1]; /* Read SNC_0 */
    serialnumber[7]=buf[2]; /* Read SNA_1 */
    serialnumber[6]=buf[3]; /* Read SNA_0 */
    //dump_buf("SHT2x Serial number: ", serialnumber, 8);
    return 0;
}
#elif (defined I2C_API_IOCTL)  /* Use I2C userspace driver read/write API */
int sht2x_softreset(int fd)
{
    struct i2c_msg               msg;
    struct i2c_rdwr_ioctl_data   sht2x_data;
    uint8_t                      buf[2];
    if( fd<0 )
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    msg.addr= 0x40;
    msg.flags=0; //write
    msg.len= 1;
    msg.buf= buf;
    msg.buf[0]=SOFTRESET;
    sht2x_data.nmsgs= 1;
    sht2x_data.msgs= &msg;
    if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
    {
        log_err("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
        sht2x_term();
        return -2;
    }
    msleep(50);
    return 0;
}
int sht2x_init(void)
{
    if( (s_sht2x_fd=open("/dev/i2c-1", O_RDWR)) < 0)
    {
        log_err("i2c device open failed: %s\n", strerror(errno));
        return -1;
    }
    if( sht2x_softreset(s_sht2x_fd) < 0 )
    {
        log_err("SHT2x softreset failure\n");
        sht2x_term();
        return -2;
    }
    return 0;
}
int sht2x_get_serialnumber(uint8_t *serialnumber, int size)
{
    struct i2c_msg               msgs[2];
    struct i2c_rdwr_ioctl_data   sht2x_data;
    uint8_t                      sbuf[2];
    uint8_t                      rbuf[4];
    if( !serialnumber || size!=8 )
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    if( s_sht2x_fd < 0 )
    {
        if( sht2x_init() < 0)
        {
            log_err("SHT2x initialise failure\n");
            return -2;
        }
    }
    /*+------------------------------------------+
     *|     Read SerialNumber from Location 1    |
     *+------------------------------------------+*/
    msgs[0].addr= 0x40;
    msgs[0].flags=0; //write
    msgs[0].len= 2;
    msgs[0].buf= sbuf;
    msgs[0].buf[0]=0xfa;  /* command for readout on-chip memory */
    msgs[0].buf[1]=0x0f;  /* on-chip memory address */
    msgs[1].addr=0x40;
    msgs[1].flags=I2C_M_RD; //write
    msgs[1].len= 4;
    msgs[1].buf= rbuf;
    sht2x_data.nmsgs= 2;
    sht2x_data.msgs= msgs;
    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
    {
        log_err("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
        sht2x_term();
        return -2;
    }
    serialnumber[5]=rbuf[0]; /* Read SNB_3 */
    serialnumber[4]=rbuf[1]; /* Read SNB_2 */
    serialnumber[3]=rbuf[2]; /* Read SNB_1 */
    serialnumber[2]=rbuf[3]; /* Read SNB_0 */
    /*+------------------------------------------+
     *|     Read SerialNumber from Location 2    |
     *+------------------------------------------+*/
    msgs[0].addr= 0x40;
    msgs[0].flags=0; //write
    msgs[0].len= 2;
    msgs[0].buf= sbuf;
    msgs[0].buf[0]=0xfc;  /* command for readout on-chip memory */
    msgs[0].buf[1]=0xc9;  /* on-chip memory address */
    msgs[1].addr=0x40;
    msgs[1].flags=I2C_M_RD; //write
    msgs[1].len= 4;
    msgs[1].buf= rbuf;
    sht2x_data.nmsgs= 2;
    sht2x_data.msgs= msgs;
    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
    {
        log_err("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
        sht2x_term();
        return -2;
    }
    serialnumber[1]=rbuf[0]; /* Read SNC_1 */
    serialnumber[0]=rbuf[1]; /* Read SNC_0 */
    serialnumber[7]=rbuf[2]; /* Read SNA_1 */
    serialnumber[6]=rbuf[3]; /* Read SNA_0 */
    //dump_buf("SHT2x Serial number: ", serialnumber, 8);
    return 0;
}
int sht2x_get_temp_humidity(float *temp, float *rh)
{
    struct i2c_msg               msg;
    struct i2c_rdwr_ioctl_data   sht2x_data;
    uint8_t                      buf[4];
    if( !temp || !rh )
    {
        log_err("Invalid input arguments\n");
        return -1;
    }
    if( s_sht2x_fd < 0 )
    {
        if( sht2x_init() < 0)
        {
            log_err("SHT2x initialise failure\n");
            return -2;
        }
    }
    /*+------------------------------------------+
     *|       measure and get temperature        |
     *+------------------------------------------+*/
    msg.addr= 0x40;
    msg.flags=0; //write
    msg.len= 1;
    msg.buf= buf;
    msg.buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;  /* trigger temperature without hold I2C bus */
    sht2x_data.nmsgs= 1;
    sht2x_data.msgs= &msg;
    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
    {
        log_err("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
        sht2x_term();
        return -2;
    }
    msleep(85);
    memset(buf, 0, sizeof(buf));
    msg.addr=0x40;
    msg.flags=I2C_M_RD; //write
    msg.len= 3;
    msg.buf= buf;
    sht2x_data.nmsgs= 1;
    sht2x_data.msgs= &msg;
    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
    {
        log_err("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
        sht2x_term();
        return -2;
    }
    //dump_buf("Temperature sample data: ", buf, 3);
    *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
    /*+------------------------------------------+
     *|    measure and get relative humidity     |
     *+------------------------------------------+*/
    msg.addr= 0x40;
    msg.flags=0; //write
    msg.len= 1;
    msg.buf= buf;
    msg.buf[0]=TRIGGER_HUMIDITY_NO_HOLD;  /* trigger humidity without hold I2C bus */
    sht2x_data.nmsgs= 1;
    sht2x_data.msgs= &msg;
    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
    {
        log_err("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
        sht2x_term();
        return -2;
    }
    msleep(29);
    memset(buf, 0, sizeof(buf));
    msg.addr=0x40;
    msg.flags=I2C_M_RD; //write
    msg.len= 3;
    msg.buf= buf;
    sht2x_data.nmsgs= 1;
    sht2x_data.msgs= &msg;
    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
    {
        log_err("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
        sht2x_term();
        return -2;
    }
    //dump_buf("Relative humidity sample data: ", buf, 3);
           *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
    return 0;
}
#endif
iotd/hal/sht20.h
New file
@@ -0,0 +1,33 @@
/*********************************************************************************
 *      Copyright:  (C) 2018 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  sht20.c
 *    Description:  This file is temperature and relative humidity sensor SHT20 code
 *
 *        Version:  1.0.0(2018/10/14)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
 *
 ********************************************************************************/
#ifndef __SHT20_H
#define __SHT20_H
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#define SOFTRESET                        0xFE
#define TRIGGER_TEMPERATURE_NO_HOLD      0xF3
#define TRIGGER_HUMIDITY_NO_HOLD         0xF5
//#define I2C_API_IOCTL  /* Use I2C userspace driver ioctl API */
#define I2C_API_RDWR /* Use I2C userspace driver read/write API */
int sht2x_init(void);
int sht2x_get_serialnumber(uint8_t *serialnumber, int size);
int sht2x_get_temp_humidity(float *temp, float *rh);
void sht2x_term(void);
#endif
iotd/hal/tsl2561.c
New file
@@ -0,0 +1,199 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  tsl2561.c
 *    Description:  This file is the Lux sensor TSL2561 API functions on RaspberryPi,
 *                  which connected to I2C-1
 *
 *        Version:  1.0.0(04/07/19)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "04/07/19 17:39:38"
 *
 ********************************************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "lylib/logger.h"
#include "lylib/util_time.h"
#include "tsl2561.h"
int s_tsl_fd = -1;
static const int  regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH};
int tsl2561_init(void)
{
    if(s_tsl_fd > 0)
        return 0;
    if( (s_tsl_fd=open("/dev/i2c-1", O_RDWR)) < 0)
    {
        log_err("TSL2561 I2C device setup failure: %s\n", strerror(errno));
    return -1;
    }
    log_nrml("TSL2561 initialise ok, s_tsl_fd=%d\n", s_tsl_fd);
    return s_tsl_fd;
}
void tsl2561_term(void)
{
    close(s_tsl_fd);
    s_tsl_fd = -1;
    log_warn("Terminate TSL2561.\n");
}
#define ON          1
#define OFF         0
int tsl2561_power(int cmd)
{
    struct i2c_msg               msg;
    struct i2c_rdwr_ioctl_data   data;
    unsigned char                buf[2];
    msg.addr= TSL2561_I2C_ADDR;
    msg.flags=0;  /* write */
    msg.len= 1;
    msg.buf= buf;
    data.nmsgs= 1;
    data.msgs= &msg;
    msg.buf[0]=CONTROL_REG;
    if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 )
    {
        log_err("%s() ioctl failure: %s\n", __func__, strerror(errno));
    return -1;
    }
    if( cmd )
        msg.buf[0]=POWER_UP;
    else
        msg.buf[0]=POWER_DOWN;
    if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 )
    {
        log_err("%s() ioctl failure: %s\n", __func__, strerror(errno));
    return -1;
    }
    return 0;
}
int tsl2561_readreg(unsigned char regaddr, unsigned char *regval)
{
    struct i2c_msg               msg;
    struct i2c_rdwr_ioctl_data   data;
    unsigned char                buf[2];
    msg.addr= TSL2561_I2C_ADDR;
    msg.flags=0;  /* write */
    msg.len= 1;
    msg.buf= buf;
    msg.buf[0] = regaddr;
    data.nmsgs= 1;
    data.msgs= &msg;
    if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 )
    {
        log_err("%s() ioctl failure: %s\n", __func__, strerror(errno));
    return -1;
    }
    memset(buf, 0, sizeof(buf));
    msg.addr= TSL2561_I2C_ADDR;
    msg.flags=I2C_M_RD;  /* read */
    msg.len= 1;
    msg.buf= buf;
    data.nmsgs= 1;
    data.msgs= &msg;
    if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 )
    {
        log_err("%s() ioctl failure: %s\n", __func__, strerror(errno));
    return -1;
    }
    *regval = msg.buf[0];
    return 0;
}
float tsl2561_get_lux(void)
{
    int                 i;
    unsigned char       reg_data[REG_COUNT];
    unsigned char       buf;
    int                 chn0_data = 0;
    int                 chn1_data = 0;
    float               div = 0.0;
    float               lux = 0.0;
    tsl2561_power(ON);
    msleep(410);  /* t(CONV) MAX 400ms */
    /* Read register Channel0 and channel1 data from register */
    for(i=0; i<REG_COUNT; i++)
    {
    tsl2561_readreg(regs_addr[i], &reg_data[i]);
    }
    chn0_data = reg_data[1]*256 + reg_data[0]; /* Channel0 = DATA0HIGH<<8 + DATA0LOW  */
    chn1_data = reg_data[3]*256 + reg_data[2]; /* channel1 = DATA1HIGH<<8 +  DATA1LOW */
    if( chn0_data<=0 || chn1_data<0 )
    {
        lux = 0.0;
        goto OUT;
    }
    div = (float)chn1_data / (float)chn0_data;
    if( div>0 && div<=0.5 )
        lux = 0.304*chn0_data-0.062*chn0_data*pow(div,1.4);
    else if( div>0.5 && div<=0.61 )
        lux = 0.0224*chn0_data-0.031*chn1_data;
    else if( div>0.61 && div<=0.8 )
        lux = 0.0128*chn0_data-0.0153*chn1_data;
    else if( div>0.8 && div<=1.3 )
        lux = 0.00146*chn0_data-0.00112*chn1_data;
    else if( div>1.3 )
        lux = 0.0;
    log_dbg("TSLl2561 get lux: %.3f\n", lux);
OUT:
    tsl2561_power(OFF);
    return lux;
}
iotd/hal/tsl2561.h
New file
@@ -0,0 +1,42 @@
/********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  tsl2561.h
 *    Description:  This head file is the Lux sensor TSL2561 API functions on RaspberryPi
 *
 *        Version:  1.0.0(04/07/19)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "04/07/19 17:42:35"
 *
 ********************************************************************************/
#ifndef  _TSL2561_H_
#define  _TSL2561_H_
#define TSL2561_I2C_ADDR                0x39
#define CONTROL_REG                     0x80
#define REG_COUNT                       4
#define POWER_UP                        0x03
#define POWER_DOWN                      0x00
/* Register Address  */
enum
{
    /* Channel_0 = DATA0HIGH<<8 + DATA0LOW */
    DATA0LOW = 0x8C,
    DATA0HIGH,
    /* Channel_1 = DATA1HIGH<<8 + DATA1LOW */
    DATA1LOW,
    DATA1HIGH,
};
extern int tsl2561_init(void);
extern void tsl2561_term(void);
extern float tsl2561_get_lux(void);
#endif   /* ----- #ifndef _TSL2561_H_  ----- */
iotd/lylib/ini_dictionary.c
New file
@@ -0,0 +1,398 @@
/*-------------------------------------------------------------------------*/
/**
   @file    ini_dictionary.c
   @author  N. Devillard
   @brief   Implements a dictionary for string variables.
   This module implements a simple dictionary object, i.e. a list
   of string/string associations. This object is useful to store e.g.
   informations retrieved from a configuration file (ini files).
*/
/*--------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
                                Includes
 ---------------------------------------------------------------------------*/
#include "ini_dictionary.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/** Maximum value size for integers and doubles. */
#define MAXVALSZ    1024
/** Minimal allocated number of entries in a dictionary */
#define DICTMINSZ   128
/** Invalid key token */
#define DICT_INVALID_KEY    ((char*)-1)
/*---------------------------------------------------------------------------
                            Private functions
 ---------------------------------------------------------------------------*/
/* Doubles the allocated size associated to a pointer */
/* 'size' is the current allocated size. */
static void * mem_double(void * ptr, int size)
{
    void * newptr ;
    newptr = calloc(2*size, 1);
    if (newptr==NULL) {
        return NULL ;
    }
    memcpy(newptr, ptr, size);
    free(ptr);
    return newptr ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Duplicate a string
  @param    s String to duplicate
  @return   Pointer to a newly allocated string, to be freed with free()
  This is a replacement for strdup(). This implementation is provided
  for systems that do not have it.
 */
/*--------------------------------------------------------------------------*/
static char * xstrdup(const char * s)
{
    char * t ;
    if (!s)
        return NULL ;
    t = (char*)malloc(strlen(s)+1) ;
    if (t) {
        strcpy(t,s);
    }
    return t ;
}
/*---------------------------------------------------------------------------
                            Function codes
 ---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
  @brief    Compute the hash key for a string.
  @param    key     Character string to use for key.
  @return   1 unsigned int on at least 32 bits.
  This hash function has been taken from an Article in Dr Dobbs Journal.
  This is normally a collision-free function, distributing keys evenly.
  The key is stored anyway in the struct so that collision can be avoided
  by comparing the key itself in last resort.
 */
/*--------------------------------------------------------------------------*/
unsigned dictionary_hash(const char * key)
{
    int         len ;
    unsigned    hash ;
    int         i ;
    len = strlen(key);
    for (hash=0, i=0 ; i<len ; i++) {
        hash += (unsigned)key[i] ;
        hash += (hash<<10);
        hash ^= (hash>>6) ;
    }
    hash += (hash <<3);
    hash ^= (hash >>11);
    hash += (hash <<15);
    return hash ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Create a new dictionary object.
  @param    size    Optional initial size of the dictionary.
  @return   1 newly allocated dictionary objet.
  This function allocates a new dictionary object of given size and returns
  it. If you do not know in advance (roughly) the number of entries in the
  dictionary, give size=0.
 */
/*--------------------------------------------------------------------------*/
dictionary * dictionary_new(int size)
{
    dictionary  *   d ;
    /* If no size was specified, allocate space for DICTMINSZ */
    if (size<DICTMINSZ) size=DICTMINSZ ;
    if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
        return NULL;
    }
    d->size = size ;
    d->val  = (char **)calloc(size, sizeof(char*));
    d->key  = (char **)calloc(size, sizeof(char*));
    d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
    return d ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Delete a dictionary object
  @param    d   dictionary object to deallocate.
  @return   void
  Deallocate a dictionary object and all memory associated to it.
 */
/*--------------------------------------------------------------------------*/
void dictionary_del(dictionary * d)
{
    int     i ;
    if (d==NULL) return ;
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]!=NULL)
            free(d->key[i]);
        if (d->val[i]!=NULL)
            free(d->val[i]);
    }
    free(d->val);
    free(d->key);
    free(d->hash);
    free(d);
    return ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get a value from a dictionary.
  @param    d       dictionary object to search.
  @param    key     Key to look for in the dictionary.
  @param    def     Default value to return if key not found.
  @return   1 pointer to internally allocated character string.
  This function locates a key in a dictionary and returns a pointer to its
  value, or the passed 'def' pointer if no such key can be found in
  dictionary. The returned character pointer points to data internal to the
  dictionary object, you should not try to free it or modify it.
 */
/*--------------------------------------------------------------------------*/
char * dictionary_get(dictionary * d, const char * key, char * def)
{
    unsigned    hash ;
    int         i ;
    hash = dictionary_hash(key);
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        /* Compare hash */
        if (hash==d->hash[i]) {
            /* Compare string, to avoid hash collisions */
            if (!strcmp(key, d->key[i])) {
                return d->val[i] ;
            }
        }
    }
    return def ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Set a value in a dictionary.
  @param    d       dictionary object to modify.
  @param    key     Key to modify or add.
  @param    val     Value to add.
  @return   int     0 if Ok, anything else otherwise
  If the given key is found in the dictionary, the associated value is
  replaced by the provided one. If the key cannot be found in the
  dictionary, it is added to it.
  It is Ok to provide a NULL value for val, but NULL values for the dictionary
  or the key are considered as errors: the function will return immediately
  in such a case.
  Notice that if you dictionary_set a variable to NULL, a call to
  dictionary_get will return a NULL value: the variable will be found, and
  its value (NULL) is returned. In other words, setting the variable
  content to NULL is equivalent to deleting the variable from the
  dictionary. It is not possible (in this implementation) to have a key in
  the dictionary without value.
  This function returns non-zero in case of failure.
 */
/*--------------------------------------------------------------------------*/
int dictionary_set(dictionary * d, const char * key, const char * val)
{
    int         i ;
    unsigned    hash ;
    if (d==NULL || key==NULL) return -1 ;
    /* Compute hash for this key */
    hash = dictionary_hash(key) ;
    /* Find if value is already in dictionary */
    if (d->n>0) {
        for (i=0 ; i<d->size ; i++) {
            if (d->key[i]==NULL)
                continue ;
            if (hash==d->hash[i]) { /* Same hash value */
                if (!strcmp(key, d->key[i])) {   /* Same key */
                    /* Found a value: modify and return */
                    if (d->val[i]!=NULL)
                        free(d->val[i]);
                    d->val[i] = val ? xstrdup(val) : NULL ;
                    /* Value has been modified: return */
                    return 0 ;
                }
            }
        }
    }
    /* Add a new value */
    /* See if dictionary needs to grow */
    if (d->n==d->size) {
        /* Reached maximum size: reallocate dictionary */
        d->val  = (char **)mem_double(d->val,  d->size * sizeof(char*)) ;
        d->key  = (char **)mem_double(d->key,  d->size * sizeof(char*)) ;
        d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
        if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
            /* Cannot grow dictionary */
            return -1 ;
        }
        /* Double size */
        d->size *= 2 ;
    }
    /* Insert key in the first empty slot. Start at d->n and wrap at
       d->size. Because d->n < d->size this will necessarily
       terminate. */
    for (i=d->n ; d->key[i] ; ) {
        if(++i == d->size) i = 0;
    }
    /* Copy key */
    d->key[i]  = xstrdup(key);
    d->val[i]  = val ? xstrdup(val) : NULL ;
    d->hash[i] = hash;
    d->n ++ ;
    return 0 ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Delete a key in a dictionary
  @param    d       dictionary object to modify.
  @param    key     Key to remove.
  @return   void
  This function deletes a key in a dictionary. Nothing is done if the
  key cannot be found.
 */
/*--------------------------------------------------------------------------*/
void dictionary_unset(dictionary * d, const char * key)
{
    unsigned    hash ;
    int         i ;
    if (key == NULL) {
        return;
    }
    hash = dictionary_hash(key);
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        /* Compare hash */
        if (hash==d->hash[i]) {
            /* Compare string, to avoid hash collisions */
            if (!strcmp(key, d->key[i])) {
                /* Found key */
                break ;
            }
        }
    }
    if (i>=d->size)
        /* Key not found */
        return ;
    free(d->key[i]);
    d->key[i] = NULL ;
    if (d->val[i]!=NULL) {
        free(d->val[i]);
        d->val[i] = NULL ;
    }
    d->hash[i] = 0 ;
    d->n -- ;
    return ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Dump a dictionary to an opened file pointer.
  @param    d   Dictionary to dump
  @param    f   Opened file pointer.
  @return   void
  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
  output file pointers.
 */
/*--------------------------------------------------------------------------*/
void dictionary_dump(dictionary * d, FILE * out)
{
    int     i ;
    if (d==NULL || out==NULL) return ;
    if (d->n<1) {
        fprintf(out, "empty dictionary\n");
        return ;
    }
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]) {
            fprintf(out, "%20s\t[%s]\n",
                    d->key[i],
                    d->val[i] ? d->val[i] : "UNDEF");
        }
    }
    return ;
}
/* Test code */
#ifdef TESTDIC
#define NVALS 20000
int main(int argc, char *argv[])
{
    dictionary  *   d ;
    char    *   val ;
    int         i ;
    char        cval[90] ;
    /* Allocate dictionary */
    printf("allocating...\n");
    d = dictionary_new(0);
    /* Set values in dictionary */
    printf("setting %d values...\n", NVALS);
    for (i=0 ; i<NVALS ; i++) {
        sprintf(cval, "%04d", i);
        dictionary_set(d, cval, "salut");
    }
    printf("getting %d values...\n", NVALS);
    for (i=0 ; i<NVALS ; i++) {
        sprintf(cval, "%04d", i);
        val = dictionary_get(d, cval, DICT_INVALID_KEY);
        if (val==DICT_INVALID_KEY) {
            printf("cannot get value for key [%s]\n", cval);
        }
    }
    printf("unsetting %d values...\n", NVALS);
    for (i=0 ; i<NVALS ; i++) {
        sprintf(cval, "%04d", i);
        dictionary_unset(d, cval);
    }
    if (d->n != 0) {
        printf("error deleting values\n");
    }
    printf("deallocating...\n");
    dictionary_del(d);
    return 0 ;
}
#endif
/* vim: set ts=4 et sw=4 tw=75 */
iotd/lylib/ini_dictionary.h
New file
@@ -0,0 +1,165 @@
/*-------------------------------------------------------------------------*/
/**
   @file    ini_dictionary.h
   @author  N. Devillard
   @brief   Implements a dictionary for string variables.
   This module implements a simple dictionary object, i.e. a list
   of string/string associations. This object is useful to store e.g.
   informations retrieved from a configuration file (ini files).
*/
/*--------------------------------------------------------------------------*/
#ifndef _INI_DICTIONARY_H_
#define _INI_DICTIONARY_H_
/*---------------------------------------------------------------------------
                                Includes
 ---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*---------------------------------------------------------------------------
                                New types
 ---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
  @brief    Dictionary object
  This object contains a list of string/string associations. Each
  association is identified by a unique string key. Looking up values
  in the dictionary is speeded up by the use of a (hopefully collision-free)
  hash function.
 */
/*-------------------------------------------------------------------------*/
typedef struct _dictionary_ {
    int             n ;     /** Number of entries in dictionary */
    int             size ;  /** Storage size */
    char        **  val ;   /** List of string values */
    char        **  key ;   /** List of string keys */
    unsigned     *  hash ;  /** List of hash values for keys */
} dictionary ;
/*---------------------------------------------------------------------------
                            Function prototypes
 ---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
  @brief    Compute the hash key for a string.
  @param    key     Character string to use for key.
  @return   1 unsigned int on at least 32 bits.
  This hash function has been taken from an Article in Dr Dobbs Journal.
  This is normally a collision-free function, distributing keys evenly.
  The key is stored anyway in the struct so that collision can be avoided
  by comparing the key itself in last resort.
 */
/*--------------------------------------------------------------------------*/
unsigned dictionary_hash(const char * key);
/*-------------------------------------------------------------------------*/
/**
  @brief    Create a new dictionary object.
  @param    size    Optional initial size of the dictionary.
  @return   1 newly allocated dictionary objet.
  This function allocates a new dictionary object of given size and returns
  it. If you do not know in advance (roughly) the number of entries in the
  dictionary, give size=0.
 */
/*--------------------------------------------------------------------------*/
dictionary * dictionary_new(int size);
/*-------------------------------------------------------------------------*/
/**
  @brief    Delete a dictionary object
  @param    d   dictionary object to deallocate.
  @return   void
  Deallocate a dictionary object and all memory associated to it.
 */
/*--------------------------------------------------------------------------*/
void dictionary_del(dictionary * vd);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get a value from a dictionary.
  @param    d       dictionary object to search.
  @param    key     Key to look for in the dictionary.
  @param    def     Default value to return if key not found.
  @return   1 pointer to internally allocated character string.
  This function locates a key in a dictionary and returns a pointer to its
  value, or the passed 'def' pointer if no such key can be found in
  dictionary. The returned character pointer points to data internal to the
  dictionary object, you should not try to free it or modify it.
 */
/*--------------------------------------------------------------------------*/
char * dictionary_get(dictionary * d, const char * key, char * def);
/*-------------------------------------------------------------------------*/
/**
  @brief    Set a value in a dictionary.
  @param    d       dictionary object to modify.
  @param    key     Key to modify or add.
  @param    val     Value to add.
  @return   int     0 if Ok, anything else otherwise
  If the given key is found in the dictionary, the associated value is
  replaced by the provided one. If the key cannot be found in the
  dictionary, it is added to it.
  It is Ok to provide a NULL value for val, but NULL values for the dictionary
  or the key are considered as errors: the function will return immediately
  in such a case.
  Notice that if you dictionary_set a variable to NULL, a call to
  dictionary_get will return a NULL value: the variable will be found, and
  its value (NULL) is returned. In other words, setting the variable
  content to NULL is equivalent to deleting the variable from the
  dictionary. It is not possible (in this implementation) to have a key in
  the dictionary without value.
  This function returns non-zero in case of failure.
 */
/*--------------------------------------------------------------------------*/
int dictionary_set(dictionary * vd, const char * key, const char * val);
/*-------------------------------------------------------------------------*/
/**
  @brief    Delete a key in a dictionary
  @param    d       dictionary object to modify.
  @param    key     Key to remove.
  @return   void
  This function deletes a key in a dictionary. Nothing is done if the
  key cannot be found.
 */
/*--------------------------------------------------------------------------*/
void dictionary_unset(dictionary * d, const char * key);
/*-------------------------------------------------------------------------*/
/**
  @brief    Dump a dictionary to an opened file pointer.
  @param    d   Dictionary to dump
  @param    f   Opened file pointer.
  @return   void
  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
  output file pointers.
 */
/*--------------------------------------------------------------------------*/
void dictionary_dump(dictionary * d, FILE * out);
#endif
iotd/lylib/ini_parser.c
New file
@@ -0,0 +1,807 @@
/*-------------------------------------------------------------------------*/
/**
   @file    cp_iniparser.c
   @author  N. Devillard
   @brief   Parser for ini files.
*/
/*--------------------------------------------------------------------------*/
/*---------------------------- Includes ------------------------------------*/
#include <ctype.h>
#include "ini_parser.h"
/*---------------------------- Defines -------------------------------------*/
#define ASCIILINESZ         (1024)
#define INI_INVALID_KEY     ((char*)-1)
/*---------------------------------------------------------------------------
                        Private to this module
 ---------------------------------------------------------------------------*/
/**
 * This enum stores the status for each parsed line (internal use only).
 */
typedef enum _line_status_ {
    LINE_UNPROCESSED,
    LINE_ERROR,
    LINE_EMPTY,
    LINE_COMMENT,
    LINE_SECTION,
    LINE_VALUE
} line_status ;
/*-------------------------------------------------------------------------*/
/**
  @brief    Convert a string to lowercase.
  @param    s   String to convert.
  @return   ptr to statically allocated string.
  This function returns a pointer to a statically allocated string
  containing a lowercased version of the input string. Do not free
  or modify the returned string! Since the returned string is statically
  allocated, it will be modified at each function call (not re-entrant).
 */
/*--------------------------------------------------------------------------*/
static char * strlwc(const char * s)
{
    static char l[ASCIILINESZ+1];
    int i ;
    if (s==NULL) return NULL ;
    memset(l, 0, ASCIILINESZ+1);
    i=0 ;
    while (s[i] && i<ASCIILINESZ) {
        l[i] = (char)tolower((int)s[i]);
        i++ ;
    }
    l[ASCIILINESZ]=(char)0;
    return l ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Remove blanks at the beginning and the end of a string.
  @param    s   String to parse.
  @return   ptr to statically allocated string.
  This function returns a pointer to a statically allocated string,
  which is identical to the input string, except that all blank
  characters at the end and the beg. of the string have been removed.
  Do not free or modify the returned string! Since the returned string
  is statically allocated, it will be modified at each function call
  (not re-entrant).
 */
/*--------------------------------------------------------------------------*/
static char * strstrip(const char * s)
{
    static char l[ASCIILINESZ+1];
    char * last ;
    if (s==NULL) return NULL ;
    while (isspace((int)*s) && *s) s++;
    memset(l, 0, ASCIILINESZ+1);
    strcpy(l, s);
    last = l + strlen(l);
    while (last > l) {
        if (!isspace((int)*(last-1)))
            break ;
        last -- ;
    }
    *last = (char)0;
    return (char*)l ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get number of sections in a dictionary
  @param    d   Dictionary to examine
  @return   int Number of sections found in dictionary
  This function returns the number of sections found in a dictionary.
  The test to recognize sections is done on the string stored in the
  dictionary: a section name is given as "section" whereas a key is
  stored as "section:key", thus the test looks for entries that do not
  contain a colon.
  This clearly fails in the case a section name contains a colon, but
  this should simply be avoided.
  This function returns -1 in case of error.
 */
/*--------------------------------------------------------------------------*/
int iniparser_getnsec(dictionary * d)
{
    int i ;
    int nsec ;
    if (d==NULL) return -1 ;
    nsec=0 ;
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        if (strchr(d->key[i], ':')==NULL) {
            nsec ++ ;
        }
    }
    return nsec ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get name for section n in a dictionary.
  @param    d   Dictionary to examine
  @param    n   Section number (from 0 to nsec-1).
  @return   Pointer to char string
  This function locates the n-th section in a dictionary and returns
  its name as a pointer to a string statically allocated inside the
  dictionary. Do not free or modify the returned string!
  This function returns NULL in case of error.
 */
/*--------------------------------------------------------------------------*/
char * iniparser_getsecname(dictionary * d, int n)
{
    int i ;
    int foundsec ;
    if (d==NULL || n<0) return NULL ;
    foundsec=0 ;
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        if (strchr(d->key[i], ':')==NULL) {
            foundsec++ ;
            if (foundsec>n)
                break ;
        }
    }
    if (foundsec<=n) {
        return NULL ;
    }
    return d->key[i] ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Dump a dictionary to an opened file pointer.
  @param    d   Dictionary to dump.
  @param    f   Opened file pointer to dump to.
  @return   void
  This function prints out the contents of a dictionary, one element by
  line, onto the provided file pointer. It is OK to specify @c stderr
  or @c stdout as output files. This function is meant for debugging
  purposes mostly.
 */
/*--------------------------------------------------------------------------*/
void iniparser_dump(dictionary * d, FILE * f)
{
    int     i ;
    if (d==NULL || f==NULL) return ;
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        if (d->val[i]!=NULL) {
            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
        } else {
            fprintf(f, "[%s]=UNDEF\n", d->key[i]);
        }
    }
    return ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Save a dictionary to a loadable ini file
  @param    d   Dictionary to dump
  @param    f   Opened file pointer to dump to
  @return   void
  This function dumps a given dictionary into a loadable ini file.
  It is Ok to specify @c stderr or @c stdout as output files.
 */
/*--------------------------------------------------------------------------*/
void iniparser_dump_ini(dictionary * d, FILE * f)
{
    int     i ;
    int     nsec ;
    char *  secname ;
    if (d==NULL || f==NULL) return ;
    nsec = iniparser_getnsec(d);
    if (nsec<1) {
        /* No section in file: dump all keys as they are */
        for (i=0 ; i<d->size ; i++) {
            if (d->key[i]==NULL)
                continue ;
            fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
        }
        return ;
    }
    for (i=0 ; i<nsec ; i++) {
        secname = iniparser_getsecname(d, i) ;
        iniparser_dumpsection_ini(d, secname, f) ;
    }
    fprintf(f, "\n");
    return ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Save a dictionary section to a loadable ini file
  @param    d   Dictionary to dump
  @param    s   Section name of dictionary to dump
  @param    f   Opened file pointer to dump to
  @return   void
  This function dumps a given section of a given dictionary into a loadable ini
  file.  It is Ok to specify @c stderr or @c stdout as output files.
 */
/*--------------------------------------------------------------------------*/
void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
{
    int     j ;
    char    keym[ASCIILINESZ+1];
    int     seclen ;
    if (d==NULL || f==NULL) return ;
    if (! iniparser_find_entry(d, s)) return ;
    seclen  = (int)strlen(s);
    fprintf(f, "\n[%s]\n", s);
    sprintf(keym, "%s:", s);
    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
        if (!strncmp(d->key[j], keym, seclen+1)) {
            fprintf(f,
                    "%-30s = %s\n",
                    d->key[j]+seclen+1,
                    d->val[j] ? d->val[j] : "");
        }
    }
    fprintf(f, "\n");
    return ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the number of keys in a section of a dictionary.
  @param    d   Dictionary to examine
  @param    s   Section name of dictionary to examine
  @return   Number of keys in section
 */
/*--------------------------------------------------------------------------*/
int iniparser_getsecnkeys(dictionary * d, char * s)
{
    int     seclen, nkeys ;
    char    keym[ASCIILINESZ+1];
    int j ;
    nkeys = 0;
    if (d==NULL) return nkeys;
    if (! iniparser_find_entry(d, s)) return nkeys;
    seclen  = (int)strlen(s);
    sprintf(keym, "%s:", s);
    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
        if (!strncmp(d->key[j], keym, seclen+1))
            nkeys++;
    }
    return nkeys;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the number of keys in a section of a dictionary.
  @param    d   Dictionary to examine
  @param    s   Section name of dictionary to examine
  @return   pointer to statically allocated character strings
  This function queries a dictionary and finds all keys in a given section.
  Each pointer in the returned char pointer-to-pointer is pointing to
  a string allocated in the dictionary; do not free or modify them.
  This function returns NULL in case of error.
 */
/*--------------------------------------------------------------------------*/
char ** iniparser_getseckeys(dictionary * d, char * s)
{
    char **keys;
    int i, j ;
    char    keym[ASCIILINESZ+1];
    int     seclen, nkeys ;
    keys = NULL;
    if (d==NULL) return keys;
    if (! iniparser_find_entry(d, s)) return keys;
    nkeys = iniparser_getsecnkeys(d, s);
    keys = (char**) malloc(nkeys*sizeof(char*));
    seclen  = (int)strlen(s);
    sprintf(keym, "%s:", s);
    i = 0;
    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
        if (!strncmp(d->key[j], keym, seclen+1)) {
            keys[i] = d->key[j];
            i++;
        }
    }
    return keys;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key
  @param    d       Dictionary to search
  @param    key     Key string to look for
  @param    def     Default value to return if key not found.
  @return   pointer to statically allocated character string
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the pointer passed as 'def' is returned.
  The returned char pointer is pointing to a string allocated in
  the dictionary, do not free or modify it.
 */
/*--------------------------------------------------------------------------*/
char * iniparser_getstring(dictionary * d, const char * key, char * def)
{
    char * lc_key ;
    char * sval ;
    if (d==NULL || key==NULL)
        return def ;
    lc_key = strlwc(key);
    sval = dictionary_get(d, lc_key, def);
    return sval ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to an int
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   integer
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.
  Supported values for integers include the usual C notation
  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
  are supported. Examples:
  "42"      ->  42
  "042"     ->  34 (octal -> decimal)
  "0x42"    ->  66 (hexa  -> decimal)
  Warning: the conversion may overflow in various ways. Conversion is
  totally outsourced to strtol(), see the associated man page for overflow
  handling.
  Credits: Thanks to A. Becker for suggesting strtol()
 */
/*--------------------------------------------------------------------------*/
int iniparser_getint(dictionary * d, const char * key, int notfound)
{
    char    *   str ;
    str = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (str==INI_INVALID_KEY) return notfound ;
    return (int)strtol(str, NULL, 0);
}
int iniparser_getlong(dictionary * d, const char * key, int notfound)
{
    char    *   str ;
    str = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (str==INI_INVALID_KEY) return notfound ;
    return strtol(str, NULL, 0);
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to a double
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   double
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.
 */
/*--------------------------------------------------------------------------*/
double iniparser_getdouble(dictionary * d, const char * key, double notfound)
{
    char    *   str ;
    str = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (str==INI_INVALID_KEY) return notfound ;
    return atof(str);
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to a boolean
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   integer
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.
  A true boolean is found if one of the following is matched:
  - A string starting with 'y'
  - A string starting with 'Y'
  - A string starting with 't'
  - A string starting with 'T'
  - A string starting with '1'
  A false boolean is found if one of the following is matched:
  - A string starting with 'n'
  - A string starting with 'N'
  - A string starting with 'f'
  - A string starting with 'F'
  - A string starting with '0'
  The notfound value returned if no boolean is identified, does not
  necessarily have to be 0 or 1.
 */
/*--------------------------------------------------------------------------*/
int iniparser_getboolean(dictionary * d, const char * key, int notfound)
{
    char    *   c ;
    int         ret ;
    c = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (c==INI_INVALID_KEY) return notfound ;
    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
        ret = 1 ;
    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
        ret = 0 ;
    } else {
        ret = notfound ;
    }
    return ret;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Finds out if a given entry exists in a dictionary
  @param    ini     Dictionary to search
  @param    entry   Name of the entry to look for
  @return   integer 1 if entry exists, 0 otherwise
  Finds out if a given entry exists in the dictionary. Since sections
  are stored as keys with NULL associated values, this is the only way
  of querying for the presence of sections in a dictionary.
 */
/*--------------------------------------------------------------------------*/
int iniparser_find_entry(
    dictionary  *   ini,
    const char  *   entry
)
{
    int found=0 ;
    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
        found = 1 ;
    }
    return found ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Set an entry in a dictionary.
  @param    ini     Dictionary to modify.
  @param    entry   Entry to modify (entry name)
  @param    val     New value to associate to the entry.
  @return   int 0 if Ok, -1 otherwise.
  If the given entry can be found in the dictionary, it is modified to
  contain the provided value. If it cannot be found, -1 is returned.
  It is Ok to set val to NULL.
 */
/*--------------------------------------------------------------------------*/
int iniparser_set(dictionary * ini, const char * entry, const char * val)
{
    return dictionary_set(ini, strlwc(entry), val) ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Delete an entry in a dictionary
  @param    ini     Dictionary to modify
  @param    entry   Entry to delete (entry name)
  @return   void
  If the given entry can be found, it is deleted from the dictionary.
 */
/*--------------------------------------------------------------------------*/
void iniparser_unset(dictionary * ini, const char * entry)
{
    dictionary_unset(ini, strlwc(entry));
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Load a single line from an INI file
  @param    input_line  Input line, may be concatenated multi-line input
  @param    section     Output space to store section
  @param    key         Output space to store key
  @param    value       Output space to store value
  @return   line_status value
 */
/*--------------------------------------------------------------------------*/
static line_status iniparser_line(
    char * input_line,
    char * section,
    char * key,
    char * value)
{
    line_status sta ;
    char        line[ASCIILINESZ+1];
    static char left_line[ASCIILINESZ+1];
    int         len, offset ;
    strcpy(line, strstrip(input_line));
    len = (int)strlen(line);
    sta = LINE_UNPROCESSED ;
    if (len<1) {
        /* Empty line */
        sta = LINE_EMPTY ;
        memset(input_line, 0, len);
    } else if (line[0]=='#' || line[0]==';') {
        /* Comment line */
        sta = LINE_COMMENT ;
        memset(input_line, 0, len);
    } else if (line[0]=='[') {
        /* Section name */
        sscanf(line, "[%[^]]", section);
        strcpy(section, strstrip(section));
        strcpy(section, strlwc(section));
        /* Left configure will go to next time to parser */
        offset = strlen(section) + 2;
        strcpy( left_line, strstrip(&(line[offset])) );
        strcpy( left_line, strstrip(left_line));
        if( strlen(left_line) > 0)
        {
            strcpy(input_line, left_line);
            strcat(input_line, "\n");
        }
        else
        {
            memset(input_line, 0, len);
        }
        sta = LINE_SECTION ;
    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
           ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
        char      *ptr = NULL;
        /* Usual key=value, with or without comments */
        strcpy(key, strstrip(key));
        strcpy(key, strlwc(key));
        strcpy(value, strstrip(value));
        /*
         * sscanf cannot handle '' or "" as empty values
         * this is done here
         */
        if (!strncmp(value, "\"\"", 2) || (!strncmp(value, "''", 2)) ) {
            value[0]=0 ;
        }
        ptr = strchr(line, '=');
        if('\''==*(ptr+1) || '\"'==*(ptr+1))
        {
            offset = strlen(key)+strlen(value) + 1 + 2; /* Skip $key='$val' */
        }
        else
        {
            offset = strlen(key)+strlen(value) + 1; /* Skip $key=$val */
        }
        strcpy( left_line, strstrip(&(line[offset])) );
        if( strlen(left_line) > 0)
        {
            strcpy(input_line, left_line);
            strcat(input_line, "\n");
        }
        else
        {
            memset(input_line, 0, len);
        }
        sta = LINE_VALUE ;
    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
        /*
         * Special cases:
         * key=
         * key=;
         * key=#
         */
        strcpy(key, strstrip(key));
        strcpy(key, strlwc(key));
        value[0]=0 ;
        sta = LINE_VALUE ;
    } else {
        /* Generate syntax error */
        sta = LINE_ERROR ;
        memset(input_line, 0, len);
    }
    return sta ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Parse an ini file and return an allocated dictionary object
  @param    ininame Name of the ini file to read.
  @return   Pointer to newly allocated dictionary
  This is the parser for ini files. This function is called, providing
  the name of the file to be read. It returns a dictionary object that
  should not be accessed directly, but through accessor functions
  instead.
  The returned dictionary must be freed using iniparser_freedict().
 */
/*--------------------------------------------------------------------------*/
dictionary * iniparser_load(const char * ininame)
{
    FILE * in ;
    char line    [ASCIILINESZ+1] ;
    char section [ASCIILINESZ+1] ;
    char key     [ASCIILINESZ+1] ;
    char tmp     [ASCIILINESZ+1] ;
    char val     [ASCIILINESZ+1] ;
    int  last=0 ;
    int  len ;
    int  lineno=0 ;
    int  errs=0;
    dictionary * dict ;
    if ((in=fopen(ininame, "r"))==NULL) {
        fprintf(stderr, "iniparser: cannot open %s\n", ininame);
        return NULL ;
    }
    dict = dictionary_new(0) ;
    if (!dict) {
        fclose(in);
        return NULL ;
    }
    memset(line,    0, ASCIILINESZ);
    memset(section, 0, ASCIILINESZ);
    memset(key,     0, ASCIILINESZ);
    memset(val,     0, ASCIILINESZ);
    last=0 ;
    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
        lineno++ ;
CONTINUE_PARSER:
        len = (int)strlen(line)-1;
        if (len==0)
            continue;
        /* Safety check against buffer overflows */
        if (line[len]!='\n') {
            fprintf(stderr,
                    "iniparser: input line too long in %s (%d)\n",
                    ininame,
                    lineno);
            dictionary_del(dict);
            fclose(in);
            return NULL ;
        }
        /* Get rid of \n and spaces at end of line */
        while ((len>=0) &&
                ((line[len]=='\n') || (isspace(line[len])))) {
            line[len]=0 ;
            len-- ;
        }
        /* Detect multi-line */
        if (line[len]=='\\') {
            /* Multi-line value */
            last=len ;
            continue ;
        } else {
            last=0 ;
        }
        switch ( iniparser_line(line, section, key, val) ) {
            case LINE_EMPTY:
            case LINE_COMMENT:
            break ;
            case LINE_SECTION:
            errs = dictionary_set(dict, section, NULL);
            break ;
            case LINE_VALUE:
            sprintf(tmp, "%s:%s", section, key);
            errs = dictionary_set(dict, tmp, val) ;
            break ;
            case LINE_ERROR:
            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
                    ininame,
                    lineno);
            fprintf(stderr, "-> %s\n", line);
            errs++ ;
            break;
            default:
            break ;
        }
        if( strlen(line) > 0)
        {
            goto CONTINUE_PARSER;
        }
        memset(line, 0, ASCIILINESZ);
        last=0;
        if (errs<0) {
            fprintf(stderr, "iniparser: memory allocation failure\n");
            break ;
        }
    }
    if (errs) {
        dictionary_del(dict);
        dict = NULL ;
    }
    fclose(in);
    return dict ;
}
/*-------------------------------------------------------------------------*/
/**
  @brief    Free all memory associated to an ini dictionary
  @param    d Dictionary to free
  @return   void
  Free all memory associated to an ini dictionary.
  It is mandatory to call this function before the dictionary object
  gets out of the current context.
 */
/*--------------------------------------------------------------------------*/
void iniparser_freedict(dictionary * d)
{
    dictionary_del(d);
}
/* vim: set ts=4 et sw=4 tw=75 */
iotd/lylib/ini_parser.h
New file
@@ -0,0 +1,308 @@
/*-------------------------------------------------------------------------*/
/**
   @file    ini_parser.h
   @author  N. Devillard
   @brief   Parser for ini files.
*/
/*--------------------------------------------------------------------------*/
#ifndef _INI_PARSER_H_
#define _INI_PARSER_H_
/*---------------------------------------------------------------------------
                                Includes
 ---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
 * The following #include is necessary on many Unixes but not Linux.
 * It is not needed for Windows platforms.
 * Uncomment it if needed.
 */
/* #include <unistd.h> */
#include "ini_dictionary.h"
/*-------------------------------------------------------------------------*/
/**
  @brief    Get number of sections in a dictionary
  @param    d   Dictionary to examine
  @return   int Number of sections found in dictionary
  This function returns the number of sections found in a dictionary.
  The test to recognize sections is done on the string stored in the
  dictionary: a section name is given as "section" whereas a key is
  stored as "section:key", thus the test looks for entries that do not
  contain a colon.
  This clearly fails in the case a section name contains a colon, but
  this should simply be avoided.
  This function returns -1 in case of error.
 */
/*--------------------------------------------------------------------------*/
int iniparser_getnsec(dictionary * d);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get name for section n in a dictionary.
  @param    d   Dictionary to examine
  @param    n   Section number (from 0 to nsec-1).
  @return   Pointer to char string
  This function locates the n-th section in a dictionary and returns
  its name as a pointer to a string statically allocated inside the
  dictionary. Do not free or modify the returned string!
  This function returns NULL in case of error.
 */
/*--------------------------------------------------------------------------*/
char * iniparser_getsecname(dictionary * d, int n);
/*-------------------------------------------------------------------------*/
/**
  @brief    Save a dictionary to a loadable ini file
  @param    d   Dictionary to dump
  @param    f   Opened file pointer to dump to
  @return   void
  This function dumps a given dictionary into a loadable ini file.
  It is Ok to specify @c stderr or @c stdout as output files.
 */
/*--------------------------------------------------------------------------*/
void iniparser_dump_ini(dictionary * d, FILE * f);
/*-------------------------------------------------------------------------*/
/**
  @brief    Save a dictionary section to a loadable ini file
  @param    d   Dictionary to dump
  @param    s   Section name of dictionary to dump
  @param    f   Opened file pointer to dump to
  @return   void
  This function dumps a given section of a given dictionary into a loadable ini
  file.  It is Ok to specify @c stderr or @c stdout as output files.
 */
/*--------------------------------------------------------------------------*/
void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
/*-------------------------------------------------------------------------*/
/**
  @brief    Dump a dictionary to an opened file pointer.
  @param    d   Dictionary to dump.
  @param    f   Opened file pointer to dump to.
  @return   void
  This function prints out the contents of a dictionary, one element by
  line, onto the provided file pointer. It is OK to specify @c stderr
  or @c stdout as output files. This function is meant for debugging
  purposes mostly.
 */
/*--------------------------------------------------------------------------*/
void iniparser_dump(dictionary * d, FILE * f);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the number of keys in a section of a dictionary.
  @param    d   Dictionary to examine
  @param    s   Section name of dictionary to examine
  @return   Number of keys in section
 */
/*--------------------------------------------------------------------------*/
int iniparser_getsecnkeys(dictionary * d, char * s);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the number of keys in a section of a dictionary.
  @param    d   Dictionary to examine
  @param    s   Section name of dictionary to examine
  @return   pointer to statically allocated character strings
  This function queries a dictionary and finds all keys in a given section.
  Each pointer in the returned char pointer-to-pointer is pointing to
  a string allocated in the dictionary; do not free or modify them.
  This function returns NULL in case of error.
 */
/*--------------------------------------------------------------------------*/
char ** iniparser_getseckeys(dictionary * d, char * s);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key
  @param    d       Dictionary to search
  @param    key     Key string to look for
  @param    def     Default value to return if key not found.
  @return   pointer to statically allocated character string
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the pointer passed as 'def' is returned.
  The returned char pointer is pointing to a string allocated in
  the dictionary, do not free or modify it.
 */
/*--------------------------------------------------------------------------*/
char * iniparser_getstring(dictionary * d, const char * key, char * def);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to an int
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   integer
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.
  Supported values for integers include the usual C notation
  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
  are supported. Examples:
  - "42"      ->  42
  - "042"     ->  34 (octal -> decimal)
  - "0x42"    ->  66 (hexa  -> decimal)
  Warning: the conversion may overflow in various ways. Conversion is
  totally outsourced to strtol(), see the associated man page for overflow
  handling.
  Credits: Thanks to A. Becker for suggesting strtol()
 */
/*--------------------------------------------------------------------------*/
int iniparser_getint(dictionary * d, const char * key, int notfound);
int iniparser_getlong(dictionary * d, const char * key, int notfound);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to a double
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   double
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.
 */
/*--------------------------------------------------------------------------*/
double iniparser_getdouble(dictionary * d, const char * key, double notfound);
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to a boolean
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   integer
  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.
  A true boolean is found if one of the following is matched:
  - A string starting with 'y'
  - A string starting with 'Y'
  - A string starting with 't'
  - A string starting with 'T'
  - A string starting with '1'
  A false boolean is found if one of the following is matched:
  - A string starting with 'n'
  - A string starting with 'N'
  - A string starting with 'f'
  - A string starting with 'F'
  - A string starting with '0'
  The notfound value returned if no boolean is identified, does not
  necessarily have to be 0 or 1.
 */
/*--------------------------------------------------------------------------*/
int iniparser_getboolean(dictionary * d, const char * key, int notfound);
/*-------------------------------------------------------------------------*/
/**
  @brief    Set an entry in a dictionary.
  @param    ini     Dictionary to modify.
  @param    entry   Entry to modify (entry name)
  @param    val     New value to associate to the entry.
  @return   int 0 if Ok, -1 otherwise.
  If the given entry can be found in the dictionary, it is modified to
  contain the provided value. If it cannot be found, -1 is returned.
  It is Ok to set val to NULL.
 */
/*--------------------------------------------------------------------------*/
int iniparser_set(dictionary * ini, const char * entry, const char * val);
/*-------------------------------------------------------------------------*/
/**
  @brief    Delete an entry in a dictionary
  @param    ini     Dictionary to modify
  @param    entry   Entry to delete (entry name)
  @return   void
  If the given entry can be found, it is deleted from the dictionary.
 */
/*--------------------------------------------------------------------------*/
void iniparser_unset(dictionary * ini, const char * entry);
/*-------------------------------------------------------------------------*/
/**
  @brief    Finds out if a given entry exists in a dictionary
  @param    ini     Dictionary to search
  @param    entry   Name of the entry to look for
  @return   integer 1 if entry exists, 0 otherwise
  Finds out if a given entry exists in the dictionary. Since sections
  are stored as keys with NULL associated values, this is the only way
  of querying for the presence of sections in a dictionary.
 */
/*--------------------------------------------------------------------------*/
int iniparser_find_entry(dictionary * ini, const char * entry) ;
/*-------------------------------------------------------------------------*/
/**
  @brief    Parse an ini file and return an allocated dictionary object
  @param    ininame Name of the ini file to read.
  @return   Pointer to newly allocated dictionary
  This is the parser for ini files. This function is called, providing
  the name of the file to be read. It returns a dictionary object that
  should not be accessed directly, but through accessor functions
  instead.
  The returned dictionary must be freed using iniparser_freedict().
 */
/*--------------------------------------------------------------------------*/
dictionary * iniparser_load(const char * ininame);
/*-------------------------------------------------------------------------*/
/**
  @brief    Free all memory associated to an ini dictionary
  @param    d Dictionary to free
  @return   void
  Free all memory associated to an ini dictionary.
  It is mandatory to call this function before the dictionary object
  gets out of the current context.
 */
/*--------------------------------------------------------------------------*/
void iniparser_freedict(dictionary * d);
#endif
iotd/lylib/linux_list.h
New file
@@ -0,0 +1,723 @@
/*********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  linux_list.h
 *    Description:  This file is copied from Linux kernel, which provide link list API.
 *
 *        Version:  1.0.0(08/09/2020)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/09/2020 02:24:34 AM"
 *
 ********************************************************************************/
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#include <linux/stddef.h>
/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
/*
 * Architectures might want to move the poison pointer offset
 * into some well-recognized area such as 0xdead000000000000,
 * that is also not mappable by user-space exploits:
 */
#ifdef CONFIG_ILLEGAL_POINTER_VALUE
# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
#else
# define POISON_POINTER_DELTA 0
#endif
/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)
#ifndef ARCH_HAS_PREFETCH
#define ARCH_HAS_PREFETCH
static inline void prefetch(const void *x) {;}
#endif
/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than
 * using the generic single-entry routines.
 */
struct list_head {
    struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}
/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new,
                  struct list_head *prev,
                  struct list_head *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
}
/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}
/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}
/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
    next->prev = prev;
    prev->next = next;
}
/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty() on entry does not return true after this, the entry is
 * in an undefined state.
 */
static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = LIST_POISON1;
    entry->prev = LIST_POISON2;
}
/**
 * list_replace - replace old entry by new one
 * @old : the element to be replaced
 * @new : the new element to insert
 *
 * If @old was empty, it will be overwritten.
 */
static inline void list_replace(struct list_head *old,
                struct list_head *new)
{
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old,
                    struct list_head *new)
{
    list_replace(old, new);
    INIT_LIST_HEAD(old);
}
/**
 * list_del_init - deletes entry from list and reinitialize it.
 * @entry: the element to delete from the list.
 */
static inline void list_del_init(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    INIT_LIST_HEAD(entry);
}
/**
 * list_move - delete from one list and add as another's head
 * @list: the entry to move
 * @head: the head that will precede our entry
 */
static inline void list_move(struct list_head *list, struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add(list, head);
}
/**
 * list_move_tail - delete from one list and add as another's tail
 * @list: the entry to move
 * @head: the head that will follow our entry
 */
static inline void list_move_tail(struct list_head *list,
                  struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add_tail(list, head);
}
/**
 * list_is_last - tests whether @list is the last entry in list @head
 * @list: the entry to test
 * @head: the head of the list
 */
static inline int list_is_last(const struct list_head *list,
                const struct list_head *head)
{
    return list->next == head;
}
/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head)
{
    return head->next == head;
}
/**
 * list_empty_careful - tests whether a list is empty and not being modified
 * @head: the list to test
 *
 * Description:
 * tests whether a list is empty _and_ checks that no other CPU might be
 * in the process of modifying either member (next or prev)
 *
 * NOTE: using list_empty_careful() without synchronization
 * can only be safe if the only activity that can happen
 * to the list entry is list_del_init(). Eg. it cannot be used
 * if another CPU could re-list_add() it.
 */
static inline int list_empty_careful(const struct list_head *head)
{
    struct list_head *next = head->next;
    return (next == head) && (next == head->prev);
}
/**
 * list_is_singular - tests whether a list has just one entry.
 * @head: the list to test.
 */
static inline int list_is_singular(const struct list_head *head)
{
    return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
{
    struct list_head *new_first = entry->next;
    list->next = head->next;
    list->next->prev = list;
    list->prev = entry;
    entry->next = list;
    head->next = new_first;
    new_first->prev = head;
}
/**
 * list_cut_position - cut a list into two
 * @list: a new list to add all removed entries
 * @head: a list with entries
 * @entry: an entry within head, could be the head itself
 *    and if so we won't cut the list
 *
 * This helper moves the initial part of @head, up to and
 * including @entry, from @head to @list. You should
 * pass on @entry an element you know is on @head. @list
 * should be an empty list or a list you do not care about
 * losing its data.
 *
 */
static inline void list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
{
    if (list_empty(head))
        return;
    if (list_is_singular(head) &&
        (head->next != entry && head != entry))
        return;
    if (entry == head)
        INIT_LIST_HEAD(list);
    else
        __list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
                 struct list_head *prev,
                 struct list_head *next)
{
    struct list_head *first = list->next;
    struct list_head *last = list->prev;
    first->prev = prev;
    prev->next = first;
    last->next = next;
    next->prev = last;
}
/**
 * list_splice - join two lists, this is designed for stacks
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice(const struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head, head->next);
}
/**
 * list_splice_tail - join two lists, each list being a queue
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice_tail(struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head->prev, head);
}
/**
 * list_splice_init - join two lists and reinitialise the emptied list.
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * The list at @list is reinitialised
 */
static inline void list_splice_init(struct list_head *list,
                    struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head, head->next);
        INIT_LIST_HEAD(list);
    }
}
/**
 * list_splice_tail_init - join two lists and reinitialise the emptied list
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * Each of the lists is a queue.
 * The list at @list is reinitialised
 */
static inline void list_splice_tail_init(struct list_head *list,
                     struct list_head *head)
{
    if (!list_empty(list)) {
        __list_splice(list, head->prev, head);
        INIT_LIST_HEAD(list);
    }
}
/**
 * list_entry - get the struct for this entry
 * @ptr:    the &struct list_head pointer.
 * @type:    the type of the struct this is embedded in.
 * @member:    the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)
/**
 * list_first_entry - get the first element from a list
 * @ptr:    the list head to take the element from.
 * @type:    the type of the struct this is embedded in.
 * @member:    the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
    list_entry((ptr)->next, type, member)
/**
 * list_for_each    -    iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:    the head for your list.
 */
#define list_for_each(pos, head) \
    for (pos = (head)->next; prefetch(pos->next), pos != (head); \
        pos = pos->next)
/**
 * __list_for_each    -    iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:    the head for your list.
 *
 * This variant differs from list_for_each() in that it's the
 * simplest possible list iteration code, no prefetching is done.
 * Use this for code that knows the list to be very short (empty
 * or 1 entry) most of the time.
 */
#define __list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)
/**
 * list_for_each_prev    -    iterate over a list backwards
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:    the head for your list.
 */
#define list_for_each_prev(pos, head) \
    for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
        pos = pos->prev)
/**
 * list_for_each_safe - iterate over a list safe against removal of list entry
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:        another &struct list_head to use as temporary storage
 * @head:    the head for your list.
 */
#define list_for_each_safe(pos, n, head) \
    for (pos = (head)->next, n = pos->next; pos != (head); \
        pos = n, n = pos->next)
/**
 * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
 * @pos:    the &struct list_head to use as a loop cursor.
 * @n:        another &struct list_head to use as temporary storage
 * @head:    the head for your list.
 */
#define list_for_each_prev_safe(pos, n, head) \
    for (pos = (head)->prev, n = pos->prev; \
         prefetch(pos->prev), pos != (head); \
         pos = n, n = pos->prev)
/**
 * list_for_each_entry    -    iterate over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)                \
    for (pos = list_entry((head)->next, typeof(*pos), member);    \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_reverse - iterate backwards over list of given type.
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 */
#define list_for_each_entry_reverse(pos, head, member)            \
    for (pos = list_entry((head)->prev, typeof(*pos), member);    \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
 * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
 * @pos:    the type * to use as a start point
 * @head:    the head of the list
 * @member:    the name of the list_struct within the struct.
 *
 * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
 */
#define list_prepare_entry(pos, head, member) \
    ((pos) ? : list_entry(head, typeof(*pos), member))
/**
 * list_for_each_entry_continue - continue iteration over list of given type
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 *
 * Continue to iterate over list of given type, continuing after
 * the current position.
 */
#define list_for_each_entry_continue(pos, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member);    \
         prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_continue_reverse - iterate backwards from the given point
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 *
 * Start to iterate over list of given type backwards, continuing after
 * the current position.
 */
#define list_for_each_entry_continue_reverse(pos, head, member)        \
    for (pos = list_entry(pos->member.prev, typeof(*pos), member);    \
         prefetch(pos->member.prev), &pos->member != (head);    \
         pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
 * list_for_each_entry_from - iterate over list of given type from the current point
 * @pos:    the type * to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing from current position.
 */
#define list_for_each_entry_from(pos, head, member)            \
    for (; prefetch(pos->member.next), &pos->member != (head);    \
         pos = list_entry(pos->member.next, typeof(*pos), member))
/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 */
#define list_for_each_entry_safe(pos, n, head, member)            \
    for (pos = list_entry((head)->next, typeof(*pos), member),    \
        n = list_entry(pos->member.next, typeof(*pos), member);    \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_continue
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing after current point,
 * safe against removal of list entry.
 */
#define list_for_each_entry_safe_continue(pos, n, head, member)         \
    for (pos = list_entry(pos->member.next, typeof(*pos), member),        \
        n = list_entry(pos->member.next, typeof(*pos), member);        \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_from
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 *
 * Iterate over list of given type from current point, safe against
 * removal of list entry.
 */
#define list_for_each_entry_safe_from(pos, n, head, member)            \
    for (n = list_entry(pos->member.next, typeof(*pos), member);        \
         &pos->member != (head);                        \
         pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
 * list_for_each_entry_safe_reverse
 * @pos:    the type * to use as a loop cursor.
 * @n:        another type * to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the list_struct within the struct.
 *
 * Iterate backwards over list of given type, safe against removal
 * of list entry.
 */
#define list_for_each_entry_safe_reverse(pos, n, head, member)        \
    for (pos = list_entry((head)->prev, typeof(*pos), member),    \
        n = list_entry(pos->member.prev, typeof(*pos), member);    \
         &pos->member != (head);                    \
         pos = n, n = list_entry(n->member.prev, typeof(*n), member))
/*
 * Double linked lists with a single pointer list head.
 * Mostly useful for hash tables where the two pointer list head is
 * too wasteful.
 * You lose the ability to access the tail in O(1).
 */
struct hlist_head {
    struct hlist_node *first;
};
struct hlist_node {
    struct hlist_node *next, **pprev;
};
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
    h->next = NULL;
    h->pprev = NULL;
}
static inline int hlist_unhashed(const struct hlist_node *h)
{
    return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
    return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
    struct hlist_node *next = n->next;
    struct hlist_node **pprev = n->pprev;
    *pprev = next;
    if (next)
        next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
    __hlist_del(n);
    n->next = LIST_POISON1;
    n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
    if (!hlist_unhashed(n)) {
        __hlist_del(n);
        INIT_HLIST_NODE(n);
    }
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
    struct hlist_node *first = h->first;
    n->next = first;
    if (first)
        first->pprev = &n->next;
    h->first = n;
    n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
                    struct hlist_node *next)
{
    n->pprev = next->pprev;
    n->next = next;
    next->pprev = &n->next;
    *(n->pprev) = n;
}
static inline void hlist_add_after(struct hlist_node *n,
                    struct hlist_node *next)
{
    next->next = n->next;
    n->next = next;
    next->pprev = &n->next;
    if(next->next)
        next->next->pprev  = &next->next;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
    for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
         pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
    for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
         pos = n)
/**
 * hlist_for_each_entry    - iterate over list of given type
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry(tpos, pos, head, member)             \
    for (pos = (head)->first;                     \
         pos && ({ prefetch(pos->next); 1;}) &&             \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member:    the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_continue(tpos, pos, member)         \
    for (pos = (pos)->next;                         \
         pos && ({ prefetch(pos->next); 1;}) &&             \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_from - iterate over a hlist continuing from current point
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @member:    the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_from(tpos, pos, member)             \
    for (; pos && ({ prefetch(pos->next); 1;}) &&             \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = pos->next)
/**
 * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @n:        another &struct hlist_node to use as temporary storage
 * @head:    the head for your list.
 * @member:    the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry_safe(tpos, pos, n, head, member)         \
    for (pos = (head)->first;                     \
         pos && ({ n = pos->next; 1; }) &&                 \
        ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
         pos = n)
#endif
iotd/lylib/logger.c
New file
@@ -0,0 +1,416 @@
/*********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  logger.c
 *    Description:  This file is the linux infrastructural logger system library
 *
 *        Version:  1.0.0(08/08/2020~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/08/2020 04:24:01 PM"
 *
 ********************************************************************************/
#include "logger.h"
#define PRECISE_TIME_FACTOR 1000
static unsigned long log_rollback_size = LOG_ROLLBACK_NONE;
/* This library is not thread safe */
static logger_t *logger = NULL;
char *log_str[LOG_LEVEL_MAX + 1] = { "", "F", "E", "W", "N", "D", "I", "T", "M" };
#define LOG_TIME_FMT       "%Y-%m-%d %H:%M:%S"
static void log_signal_handler(int sig)
{
    if(!logger)
        return ;
    if (sig == SIGHUP)
    {
        signal(SIGHUP, log_signal_handler);
        log_fatal("SIGHUP received - reopenning log file [%s]", logger->file);
        logger_reopen();
    }
}
static void logger_banner(char *prefix)
{
    if(!logger)
        return ;
    fprintf(logger->fp, "%s log \"%s\" on level [%s] size [%lu] KiB, log system version %s\n",
            prefix, logger->file, log_str[logger->level], log_rollback_size / 1024, LOG_VERSION_STR);
#ifdef LOG_FILE_LINE
    fprintf(logger->fp, " [ Date ]    [ Time ]   [ Level ]  [ File/Line ]  [ Message ]\n");
#else
    fprintf(logger->fp, " [ Date ]    [ Time ]   [ Level ]  [ Message ]\n");
#endif
    fprintf(logger->fp, "-------------------------------------------------------------\n");
}
static void check_and_rollback(void)
{
    if(!logger)
        return ;
    if (log_rollback_size != LOG_ROLLBACK_NONE)
    {
        long _curOffset = ftell(logger->fp);
        if ((_curOffset != -1) && (_curOffset >= log_rollback_size))
        {
            char cmd[512];
            snprintf(cmd, sizeof(cmd), "cp -f %s %s.roll", logger->file, logger->file);
            system(cmd);
            if (-1 == fseek(logger->fp, 0L, SEEK_SET))
                fprintf(logger->fp, "log rollback fseek failed \n");
            rewind(logger->fp);
            truncate(logger->file, 0);
            logger_banner("Already rollback");
        }
    }
}
/* log_size unit is KB */
int logger_init(logger_t *log, char *log_file, int level, int log_size)
{
    if( !log )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    if( log_file )
    {
        strncpy(log->file, log_file, FILENAME_LEN);
        log->flag |= FLAG_LOGGER_FILE;
    }
    else
    {
        strncpy(log->file, DBG_LOG_FILE, FILENAME_LEN);
        log->flag |= FLAG_LOGGER_CONSOLE;
    }
    log->level = level;
    log->size = log_size;
    /* set static global $logger point to argument $log  */
    logger = log;
    return 0;
}
int logger_open(void)
{
    struct sigaction act;
    char *filemode;
    if(!logger)
    {
        printf("ERROR: logger not initialise\n");
        return -1;
    }
    log_rollback_size = logger->size <= 0 ? LOG_ROLLBACK_NONE : logger->size*1024;    /* Unit KiB */
    if ('\0' == logger->file[0])
    {
        printf("ERROR: Logger filename not set\n");
        return -1;
    }
    if (!strcmp(logger->file, DBG_LOG_FILE))
    {
        logger->fp = stderr;
        log_rollback_size = LOG_ROLLBACK_NONE;
        logger->flag |= FLAG_LOGGER_CONSOLE;
        goto OUT;
    }
    filemode = (log_rollback_size==LOG_ROLLBACK_NONE) ? "a+" : "w+";
    logger->fp = fopen(logger->file, filemode);
    if (NULL == logger->fp)
    {
        fprintf(stderr, "Open log file \"%s\" in %s failure: %s\n", logger->file, filemode, strerror(errno));
        return -2;
    }
    act.sa_handler = log_signal_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGHUP, &act, NULL);
  OUT:
    logger_banner("Initialize");
    return 0;
}
void logger_close(void)
{
    if (!logger || !logger->fp )
        return;
    logger_banner("\nTerminate");
    logger_raw("\n\n\n\n");
    fflush(logger->fp);
    fclose(logger->fp);
    logger->fp = NULL;
    return ;
}
int logger_reopen(void)
{
    int rc = 0;
    char *filemode;
    if( !logger )
        return -1;
    log_rollback_size = logger->size <= 0 ? LOG_ROLLBACK_NONE : logger->size*1024;    /* Unit KiB */
    if (logger->flag & FLAG_LOGGER_CONSOLE )
    {
        fflush(logger->fp);
        logger->fp = stderr;
        return 0;
    }
    if (logger->fp)
    {
        logger_close();
        filemode = log_rollback_size==LOG_ROLLBACK_NONE ? "a+" : "w+";
        logger->fp = fopen(logger->file, filemode);
        if (logger->fp == NULL)
            rc = -2;
    }
    else
    {
        rc = -3;
    }
    if (!rc)
    {
        logger_banner("\nReopen");
    }
    return rc;
}
void logger_term(void)
{
    if(!logger)
        return ;
    logger_close();
    logger = NULL;
}
void logger_raw(const char *fmt, ...)
{
    va_list argp;
    if (!logger || !logger->fp)
        return;
    check_and_rollback();
    va_start(argp, fmt);
    vfprintf(logger->fp, fmt, argp);
    va_end(argp);
}
static void cp_printout(char *level, char *fmt, va_list argp)
{
    char buf[MAX_LOG_MESSAGE_LEN];
    struct tm *local;
    struct timeval now;
    char timestr[256];
    if(!logger)
        return ;
    check_and_rollback();
    gettimeofday(&now, NULL);
    local = localtime(&now.tv_sec);
    strftime(timestr, 256, LOG_TIME_FMT, local);
    vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp);
#ifdef DUMPLICATE_OUTPUT
    printf("%s.%03ld [%s] : %s",
           timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, buf);
#endif
    if (logger->fp)
        fprintf(logger->fp, "%s.%03ld [%s] : %s", timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, buf);
    if (logger->fp)
        fflush(logger->fp);
}
static void cp_printout_line(char *level, char *fmt, char *file, int line, va_list argp)
{
    char buf[MAX_LOG_MESSAGE_LEN];
    struct tm *local;
    struct timeval now;
    char timestr[256];
    if(!logger)
        return ;
    check_and_rollback();
    gettimeofday(&now, NULL);
    local = localtime(&now.tv_sec);
    strftime(timestr, 256, LOG_TIME_FMT, local);
    vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp);
#ifdef DUMPLICATE_OUTPUT
    printf("[%s.%03ld] <%s> <%s:%04d> : %s",
           timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, file, line, buf);
#endif
    if (logger->fp)
    {
        fprintf(logger->fp, "[%s.%03ld] <%s> <%s:%04d> : %s",
                timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, file, line, buf);
        fflush(logger->fp);
    }
}
void logger_str(int level, const char *msg)
{
    if (!logger || level>logger->level)
        return;
    check_and_rollback();
    if (logger->fp)
        fwrite(msg, 1, strlen(msg), logger->fp);
    if(logger->fp)
        fflush(logger->fp);
}
void logger_min(int level, char *fmt, ...)
{
    va_list argp;
    if (!logger || level>logger->level)
        return;
    va_start(argp, fmt);
    cp_printout(log_str[level], fmt, argp);
    va_end(argp);
}
void logger_line(int level, char *file, int line, char *fmt, ...)
{
    va_list argp;
    if (!logger || level>logger->level)
        return;
    va_start(argp, fmt);
    cp_printout_line(log_str[level], fmt, file, line, argp);
    va_end(argp);
}
#define LINELEN 81
#define CHARS_PER_LINE 16
static char *print_char =
    "                "
    "                "
    " !\"#$%&'()*+,-./"
    "0123456789:;<=>?"
    "@ABCDEFGHIJKLMNO"
    "PQRSTUVWXYZ[\\]^_"
    "`abcdefghijklmno"
    "pqrstuvwxyz{|}~ "
    "                "
    "                "
    " ???????????????"
    "????????????????"
    "????????????????"
    "????????????????"
    "????????????????"
    "????????????????";
void logger_dump(int level, char *buf, int len)
{
    int rc;
    int idx;
    char prn[LINELEN];
    char lit[CHARS_PER_LINE + 2];
    char hc[4];
    short line_done = 1;
    if (!logger || level>logger->level)
        return;
    rc = len;
    idx = 0;
    lit[CHARS_PER_LINE] = '\0';
    while (rc > 0)
    {
        if (line_done)
            snprintf(prn, LINELEN, "%08X: ", idx);
        do
        {
            unsigned char c = buf[idx];
            snprintf(hc, 4, "%02X ", c);
            strncat(prn, hc, LINELEN);
            lit[idx % CHARS_PER_LINE] = print_char[c];
        }
        while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
        line_done = (idx % CHARS_PER_LINE) == 0;
        if (line_done)
        {
#ifdef DUMPLICATE_OUTPUT
            printf("%s  %s\n", prn, lit);
#endif
            if (logger->fp)
                fprintf(logger->fp, "%s  %s\n", prn, lit);
        }
    }
    if (!line_done)
    {
        int ldx = idx % CHARS_PER_LINE;
        lit[ldx++] = print_char[(int)buf[idx]];
        lit[ldx] = '\0';
        while ((++idx % CHARS_PER_LINE) != 0)
            strncat(prn, "   ", sizeof(prn));
#ifdef DUMPLICATE_OUTPUT
        printf("%s  %s\n", prn, lit);
#endif
        if (logger->fp)
            fprintf(logger->fp, "%s  %s\n", prn, lit);
    }
}
iotd/lylib/logger.h
New file
@@ -0,0 +1,113 @@
/********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  logger.h
 *    Description:  This file is the linux infrastructural logger system library
 *
 *        Version:  1.0.0(08/08/2020~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/08/2020 05:16:56 PM"
 *
 ********************************************************************************/
#ifndef  _LOGGER_H_
#define  _LOGGER_H_
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#define LOG_VERSION_STR             "1.0.0"
#ifndef FILENAME_LEN
#define FILENAME_LEN                64
#endif
#define DBG_LOG_FILE                "stderr"  /*  Debug mode log file is console */
#define LOG_ROLLBACK_SIZE           512    /* Default rollback log size  */
#define LOG_ROLLBACK_NONE           0      /* Set rollback size to 0 will not rollback  */
#define DEFAULT_TIME_FORMAT         "%Y-%m-%d %H:%M:%S"
#define MAX_LOG_MESSAGE_LEN         0x1000
//#define DUMPLICATE_OUTPUT  /* Log to file and printf on console  */
enum
{
    LOG_LEVEL_DISB = 0,               /*  Disable "Debug" */
    LOG_LEVEL_FATAL,                  /*  Debug Level "Fatal" */
    LOG_LEVEL_ERROR,                  /*  Debug Level "ERROR" */
    LOG_LEVEL_WARN,                   /*  Debug Level "warnning" */
    LOG_LEVEL_NRML,                   /*  Debug Level "Normal" */
    LOG_LEVEL_DEBUG,                  /*  Debug Level "Debug" */
    LOG_LEVEL_INFO,                   /*  Debug Level "Information" */
    LOG_LEVEL_TRACE,                  /*  Debug Level "Trace" */
    LOG_LEVEL_MAX,
};
/* logger->flag definition */
#define FLAG_LOGGER_LEVEL_OPT           1<<0 /*  The log level is sepcified by the command option */
#define FLAG_LOGGER_CONSOLE             1<<1
#define FLAG_LOGGER_FILE                0<<1
typedef struct logger_s
{
    unsigned char      flag;
    char               file[FILENAME_LEN];
    int                level;
    int                size;
    FILE               *fp;
} logger_t;
extern char *log_str[];
/* log_size unit is KB */
extern int  logger_init(logger_t *logger, char *filename, int level, int log_size);
extern int  logger_open(void);
extern void logger_set_time_format(char *time_format);
extern int  logger_reopen(void);
extern void logger_close(void);
extern void logger_term(void);
extern void logger_raw(const char *fmt, ...);
extern void logger_min(int level, char *fmt, ...);
extern void logger_line(int level, char *file, int line, char *fmt, ...);
extern void logger_str(int level, const char *msg);
extern void logger_dump(int level, char *buf, int len);
#define LOG_FILE_LINE      /* Log the file and line */
#ifdef LOG_FILE_LINE
#define log_trace(fmt, ...) logger_line(LOG_LEVEL_TRACE, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_info(fmt, ...)  logger_line(LOG_LEVEL_INFO,  __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_dbg(fmt, ...)   logger_line(LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_nrml(fmt, ...)  logger_line(LOG_LEVEL_NRML,  __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_warn(fmt, ...)  logger_line(LOG_LEVEL_WARN,  __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_err(fmt, ...)   logger_line(LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define log_fatal(fmt, ...) logger_line(LOG_LEVEL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#else
#define log_trace(fmt, ...) logger_min(LOG_LEVEL_TRACE, fmt, ##__VA_ARGS__)
#define log_info(fmt, ...)  logger_min(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define log_dbg(fmt, ...)   logger_min(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#define log_nrml(fmt, ...)  logger_min(LOG_LEVEL_NRML, fmt, ##__VA_ARGS__)
#define log_warn(fmt, ...)  logger_min(LOG_LEVEL_WARN, fmt, ##__VA_ARGS__)
#define log_err(fmt, ...)   logger_min(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#define log_fatal(fmt, ...) logger_min(LOG_LEVEL_FATAL, fmt, ##__VA_ARGS__)
#endif
#endif   /* ----- #ifndef _LOGGER_H_  ----- */
iotd/lylib/makefile
New file
@@ -0,0 +1,15 @@
PWD=$(shell pwd )
LIBNAME=$(shell basename ${PWD} )
TOPDIR=$(shell dirname ${PWD} )
all: clean
    @rm -f *.o
    @${CROSSTOOL}gcc ${CFLAGS} -I${TOPDIR} -c *.c
    ${CROSSTOOL}ar -rcs  lib${LIBNAME}.a *.o
clean:
    @rm -f *.o
    @rm -f *.a
iotd/lylib/util_proc.c
New file
@@ -0,0 +1,376 @@
/*********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  util_proc.c
 *    Description:  This file is the process API
 *
 *        Version:  1.0.0(7/06/2020)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "7/06/2020 09:19:02 PM"
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include "util_proc.h"
#include "logger.h"
proc_signal_t     g_signal={0};
void proc_default_sighandler(int sig)
{
    switch(sig)
    {
        case SIGINT:
            log_warn("SIGINT - stopping\n");
            g_signal.stop = 1;
            break;
        case SIGTERM:
            log_warn("SIGTERM - stopping\n");
            g_signal.stop = 1;
            break;
        case SIGSEGV:
            log_warn("SIGSEGV - stopping\n");
#if 0
            if(g_signal.stop)
                exit(0);
            g_signal.stop = 1;
#endif
            break;
        case SIGPIPE:
            log_warn("SIGPIPE - warnning\n");
            break;
        default:
            break;
    }
}
/* install default signal process functions  */
void install_default_signal(void)
{
    struct sigaction sigact, sigign;
    log_nrml("Install default signal handler.\n");
    /*  Initialize the catch signal structure. */
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigact.sa_handler = proc_default_sighandler;
    /*  Setup the ignore signal. */
    sigemptyset(&sigign.sa_mask);
    sigign.sa_flags = 0;
    sigign.sa_handler = SIG_IGN;
    sigaction(SIGTERM, &sigact, 0); /*  catch terminate signal "kill" command */
    sigaction(SIGINT,  &sigact, 0); /*  catch interrupt signal CTRL+C */
    //sigaction(SIGSEGV, &sigact, 0); /*  catch segmentation faults  */
    sigaction(SIGPIPE, &sigact, 0); /*  catch broken pipe */
#if 0
    sigaction(SIGCHLD, &sigact, 0); /*  catch child process return */
    sigaction(SIGUSR2, &sigact, 0); /*  catch USER signal */
#endif
}
/* ****************************************************************************
 * FunctionName: daemonize
 * Description : Set the programe runs as daemon in background
 * Inputs      : nodir: DON'T change the work directory to / :  1:NoChange 0:Change
 *               noclose: close the opened file descrtipion or not 1:Noclose 0:Close
 * Output      : NONE
 * Return      : NONE
 * *****************************************************************************/
void daemonize(int nochdir, int noclose)
{
    int retval, fd;
    int i;
    /*  already a daemon */
    if (1 == getppid())
        return;
    /*  fork error */
    retval = fork();
    if (retval < 0) exit(1);
    /*  parent process exit */
    if (retval > 0)
        exit(0);
    /*  obtain a new process session group */
    setsid();
    if (!noclose)
    {
        /*  close all descriptors */
        for (i = getdtablesize(); i >= 0; --i)
        {
            //if (i != g_logPtr->fd)
                close(i);
        }
        /*  Redirect Standard input [0] to /dev/null */
        fd = open("/dev/null", O_RDWR);
        /* Redirect Standard output [1] to /dev/null */
        dup(fd);
        /* Redirect Standard error [2] to /dev/null */
        dup(fd);
    }
    umask(0);
    if (!nochdir)
        chdir("/");
    return;
}
/* ****************************************************************************
 * FunctionName: record_daemon_pid
 * Description : Record the running daemon program PID to the file "pid_file"
 * Inputs      : pid_file:The record PID file path
 * Output      : NONE
 * Return      : 0: Record successfully  Else: Failure
 * *****************************************************************************/
int record_daemon_pid(const char *pid_file)
{
    struct stat fStatBuf;
    int fd = -1;
    int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU;
    char ipc_dir[64] = { 0 };
    strncpy(ipc_dir, pid_file, 64);
    /* dirname() will modify ipc_dir and save the result */
    dirname(ipc_dir);
    /* If folder pid_file PATH doesnot exist, then we will create it" */
    if (stat(ipc_dir, &fStatBuf) < 0)
    {
        if (mkdir(ipc_dir, mode) < 0)
        {
            log_fatal("cannot create %s: %s\n", ipc_dir, strerror(errno));
            return -1;
        }
        (void)chmod(ipc_dir, mode);
    }
    /*  Create the process running PID file */
    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    if ((fd = open(pid_file, O_RDWR | O_CREAT | O_TRUNC, mode)) >= 0)
    {
        char pid[PID_ASCII_SIZE];
        snprintf(pid, sizeof(pid), "%u\n", (unsigned)getpid());
        write(fd, pid, strlen(pid));
        close(fd);
        log_dbg("Record PID<%u> to file %s.\n", getpid(), pid_file);
    }
    else
    {
        log_fatal("cannot create %s: %s\n", pid_file, strerror(errno));
        return -1;
    }
    return 0;
}
/* ****************************************************************************
 * FunctionName: get_daemon_pid
 * Description : Get the daemon process PID from the PID record file "pid_file"
 * Inputs      : pid_file: the PID record file
 * Output      : NONE
 * Return      : pid_t: The daemon process PID number
 * *****************************************************************************/
pid_t get_daemon_pid(const char *pid_file)
{
    FILE *f;
    pid_t pid;
    if ((f = fopen(pid_file, "rb")) != NULL)
    {
        char pid_ascii[PID_ASCII_SIZE];
        (void)fgets(pid_ascii, PID_ASCII_SIZE, f);
        (void)fclose(f);
        pid = atoi(pid_ascii);
    }
    else
    {
        log_fatal("Can't open PID record file %s: %s\n", pid_file, strerror(errno));
        return -1;
    }
    return pid;
}
/* ****************************************************************************
 * FunctionName: check_daemon_running
 * Description : Check the daemon program already running or not
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 1: The daemon program alread running   0: Not running
 * *****************************************************************************/
int check_daemon_running(const char *pid_file)
{
    int retVal = -1;
    struct stat fStatBuf;
    retVal = stat(pid_file, &fStatBuf);
    if (0 == retVal)
    {
        pid_t pid = -1;
        printf("PID record file \"%s\" exist.\n", pid_file);
        pid = get_daemon_pid(pid_file);
        if (pid > 0)  /*  Process pid exist */
        {
            if ((retVal = kill(pid, 0)) == 0)
            {
                printf("Program with PID[%d] seems running.\n", pid);
                return 1;
            }
            else   /* Send signal to the old process get no reply. */
            {
                printf("Program with PID[%d] seems exit.\n", pid);
                remove(pid_file);
                return 0;
            }
        }
        else if (0 == pid)
        {
            printf("Can not read program PID form record file.\n");
            remove(pid_file);
            return 0;
        }
        else  /* Read pid from file "pid_file" failure */
        {
            printf("Read record file \"%s\" failure, maybe program still running.\n", pid_file);
            return 1;
        }
    }
    return 0;
}
/* ****************************************************************************
 * FunctionName: stop_daemon_running
 * Description : Stop the daemon program running
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 1: The daemon program alread running   0: Not running
 * *****************************************************************************/
int stop_daemon_running(const char *pid_file)
{
    pid_t            pid = -1;
    struct stat      fStatBuf;
    if ( stat(pid_file, &fStatBuf) < 0)
        return 0;
    printf("PID record file \"%s\" exist.\n", pid_file);
    pid = get_daemon_pid(pid_file);
    if (pid > 0)  /*  Process pid exist */
    {
        while ( (kill(pid, 0) ) == 0)
        {
            kill(pid, SIGTERM);
            sleep(1);
        }
        remove(pid_file);
    }
    return 0;
}
/* ****************************************************************************
 * FunctionName: set_daemon_running
 * Description : Set the programe running as daemon if it's not running and record
 *               its PID to the pid_file.
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 0: Successfully. 1: Failure
 * *****************************************************************************/
int set_daemon_running(const char *pid_file)
{
    daemonize(0, 1);
    log_nrml("Program running as daemon [PID:%d].\n", getpid());
    if (record_daemon_pid(pid_file) < 0)
    {
        log_fatal("Record PID to file \"%s\" failure.\n", pid_file);
        return -2;
    }
    return 0;
}
/* start a new thread to run $thread_workbody point function  */
int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg)
{
    int        retval = 0;
    pthread_attr_t thread_attr;
    /* Initialize the thread  attribute */
    retval = pthread_attr_init(&thread_attr);
    if(retval)
        return -1;
    /* Set the stack size of the thread */
    retval = pthread_attr_setstacksize(&thread_attr, 120 * 1024);
    if(retval)
        goto CleanUp;
    /* Set thread to detached state:Don`t need pthread_join */
    retval = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
    if(retval)
        goto CleanUp;
    /* Create the thread */
    retval = pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg);
    if(retval)
        goto CleanUp;
CleanUp:
    /* Destroy the  attributes  of  thread */
    pthread_attr_destroy(&thread_attr);
    return retval;
}
/* excute a linux command by system() */
void exec_system_cmd(const char *format, ...)
{
    char                cmd[256];
    va_list             args;
    memset(cmd, 0, sizeof(cmd));
    va_start(args, format);
    vsnprintf(cmd, sizeof(cmd), format, args);
    va_end(args);
    system(cmd);
}
iotd/lylib/util_proc.h
New file
@@ -0,0 +1,64 @@
/********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  util_proc.h
 *    Description:  This head file is for Linux process/thread API
 *
 *        Version:  1.0.0(7/06/2012~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "7/06/2012 09:21:33 PM"
 *
 ********************************************************************************/
#ifndef __UTIL_PROC_H_
#define __UTIL_PROC_H_
#include <signal.h>
#define PID_ASCII_SIZE  11
typedef struct proc_signal_s
{
    int       signal;
    unsigned  stop;     /* 0: Not term  1: Stop  */
}  proc_signal_t;
typedef void *(* thread_body_t) (void *thread_arg);
extern proc_signal_t    g_signal;
/* install default signal process functions  */
extern void install_default_signal(void);
/* excute a linux command by system() */
extern void exec_system_cmd(const char *format, ...);
/* check program already running or not from $pid_file  */
extern int check_daemon_running(const char *pid_file);
/* set program daemon running and record pid in $pid_file  */
extern int set_daemon_running(const char *pid_file);
/* record proces ID into $pid_file  */
extern int record_daemon_pid(const char *pid_file);
/* stop program running from $pid_file  */
extern int stop_daemon_running(const char *pid_file);
/* my implementation for set program running in daemon   */
extern void daemonize(int nochdir, int noclose);
/* start a new thread to run $thread_workbody point function  */
extern int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg);
/* +---------------------+
 * |   Low level API     |
 * +---------------------+*/
/* get daemon process ID from $pid_file   */
extern pid_t get_daemon_pid(const char *pid_file);
#endif
iotd/lylib/util_time.h
New file
@@ -0,0 +1,185 @@
/********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  util_time.h
 *    Description:  This head file is system time, timer API
 *
 *        Version:  1.0.0(07/23/2020)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "07/23/2020 07:46:37 AM"
 *
 ********************************************************************************/
#ifndef __UTIL_TIME_H_
#define __UTIL_TIME_H_
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
typedef struct date_time_s
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
    int dayofweek;
} date_time_t;
/* sleep for micro second */
static inline void msleep(unsigned long ms)
{
    struct timespec  timeout;
    unsigned long    tmp;
    timeout.tv_sec = ms / 1000;
    if (timeout.tv_sec == 0)
    {
        tmp = ms * 10000;
        timeout.tv_nsec = tmp * 100;
    }
    else
    {
        timeout.tv_nsec = 0;
    }
    nanosleep(&timeout, 0);
}
/* call gettimeofday() to get current micro second */
static inline unsigned long time_now()
{
    struct timeval            now;
    gettimeofday(&now, 0);
    return (now.tv_sec*1000) + (now.tv_usec/1000);
}
/* timep has elapsed since $start, unit as micro second*/
static inline uint32_t time_elapsed(uint32_t start)
{
    uint32_t current = time_now();
    if(current >= start)
        return current-start;
    else
        return current+0xFFFFFFFF-start;
}
/* call gettimeofday() to get current micro second */
static inline unsigned long time_second()
{
    struct timeval            now;
    gettimeofday(&now, 0);
    return now.tv_sec;
}
/* timep has elapsed since $start, unit as micro second*/
static inline uint32_t seconds_elapsed(uint32_t start)
{
    uint32_t current = time_second();
    if(current >= start)
        return current-start;
    else
        return current+0xFFFFFFFF-start;
}
/*
* These inlines deal with timer wrapping correctly. You are
* strongly encouraged to use them
* 1. Because people otherwise forget
* 2. Because if the timer wrap changes in future you won't have to
* alter your driver code.
*
* time_after(a,b) returns true if the time a is after time b.
*
* Do this with "<0" and ">=0" to only test the sign of the result. A
* good compiler would generate better code (and a really good compiler
* wouldn't care). Gcc is currently neither.
*/
#define typecheck(type,x) \
({      type __dummy; \
        typeof(x) __dummy2; \
        (void)(&__dummy == &__dummy2); \
        1; \
})
#define time_after(a,b) \
(typecheck(unsigned long, a) && typecheck(unsigned long, b) && ((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) \
(typecheck(unsigned long, a) && typecheck(unsigned long, b) && ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)
/* Same as above, but does so with platform independent 64bit types.
 * These must be used when utilizing jiffies_64 (i.e. return value of
 * get_jiffies_64() */
#define time_after64(a,b) \
    (typecheck(__u64, a) && typecheck(__u64, b) && ((__s64)(b) - (__s64)(a) < 0))
#define time_before64(a,b)  time_after64(b,a)
#define time_after_eq64(a,b) \
    (typecheck(__u64, a) && typecheck(__u64, b) && ((__s64)(a) - (__s64)(b) >= 0))
#define time_before_eq64(a,b)   time_after_eq64(b,a)
static inline void get_sys_time(date_time_t *date)
{
    time_t now = time(NULL);
    struct tm *tnow = localtime(&now);
    memset(date, 0, sizeof(*date));
    date->year = 1900 + tnow->tm_year;
    date->month = 1 + tnow->tm_mon;
    date->day = tnow->tm_mday;
    date->hour = tnow->tm_hour;
    date->minute = tnow->tm_min;
    date->second = tnow->tm_sec;
    date->dayofweek = tnow->tm_wday;
    return;
}
static inline int get_rtc_time(date_time_t *date)
{
    int                 rv, fd = -1;
    struct rtc_time     rtc_tm;
    memset(date, 0, sizeof(*date));
    if ((fd=open("/dev/rtc0", O_RDONLY)) < 0)
        return -1;
    if((rv=ioctl(fd, RTC_RD_TIME, &rtc_tm)) < 0)
        return -2;
    date->year = 1900 + rtc_tm.tm_year;
    date->month = 1 + rtc_tm.tm_mon;
    date->day = rtc_tm.tm_mday;
    date->hour = rtc_tm.tm_hour;
    date->minute = rtc_tm.tm_min;
    date->second = rtc_tm.tm_sec;
    date->dayofweek = rtc_tm.tm_wday;
    close(fd);
    return 0;
}
#endif
iotd/main.c
New file
@@ -0,0 +1,410 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  main.c
 *    Description:  This file
 *
 *        Version:  1.0.0(29/01/19)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "29/01/19 15:34:41"
 *
 ********************************************************************************/
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
#include <string.h>
#include <math.h>
#include <cjson/cJSON.h>
#include <mosquitto.h>
#include "lylib/util_time.h"
#include "lylib/logger.h"
#include "lylib/util_proc.h"
#include "hal/hal.h"
#include "conf/conf.h"
#define PROG_VERSION               "v1.0.0"
#define DAEMON_PIDFILE             "/tmp/.iotd.pid"
int check_set_program_running(int daemon);
void *mqtt_sub_worker(void *args);
static void program_usage(char *progname)
{
    printf("Usage: %s [OPTION]...\n", progname);
    printf(" %s is LingYun studio MQTT daemon program running on RaspberryPi\n", progname);
    printf("\nMandatory arguments to long options are mandatory for short options too:\n");
    printf(" -d[debug   ]  Running in debug mode\n");
    printf(" -c[conf    ]  Specify configure file\n");
    printf(" -h[help    ]  Display this help information\n");
    printf(" -v[version ]  Display the program version\n");
    printf("\n%s version %s\n", progname, PROG_VERSION);
    return;
}
void auto_light_off(int signum)
{
    log_nrml("turn hallway auto light off now\n");
    //turn_light(LIGHT_HALLWAY, OFF);
}
int main (int argc, char **argv)
{
    float              temp;
    float              rh;
    int                daemon = 1;
    pthread_t          tid;
    iotd_ctx_t         ctx;
    hal_ctx_t         *hal_ctx = &ctx.hal_ctx;
    char               *conf_file="/etc/iotd.conf";
    int                debug = 0;
    int                opt;
    char              *progname=NULL;
    float              lux = 0.0;
    struct option long_options[] = {
        {"conf", required_argument, NULL, 'c'},
        {"debug", no_argument, NULL, 'd'},
        {"version", no_argument, NULL, 'v'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };
    progname = (char *)basename(argv[0]);
    /* Parser the command line parameters */
    while ((opt = getopt_long(argc, argv, "c:dvh", long_options, NULL)) != -1)
    {
        switch (opt)
        {
            case 'c': /* Set configure file */
                conf_file = optarg;
                break;
            case 'd': /* Set debug running */
                daemon = 0;
                debug = 1;
                break;
            case 'v':  /* Get software version */
                printf("%s version %s\n", progname, PROG_VERSION);
                return 0;
            case 'h':  /* Get help information */
                program_usage(progname);
                return 0;
            default:
                break;
        }
    }
    if( !conf_file )
        debug = 1;
    //printf("conf_file: %s debug:%d\n", conf_file, debug);
    if( parser_conf(conf_file, &ctx, debug)<0 )
    {
        fprintf(stderr, "Parser mqtted configure file failure\n");
        return -2;
    }
    return 0;
    if( hal_init(hal_ctx) < 0 )
    {
        log_err("Initialise hardware failure\n");
        return -3;
    }
    else
    {
        log_nrml("HAL initialise ok\n");
    }
    install_default_signal();
    signal(SIGALRM, auto_light_off);
    if( check_set_program_running(daemon) < 0 )
        goto OUT;
    mosquitto_lib_init();
    if( thread_start(&tid, mqtt_sub_worker, &ctx ) < 0 )
    {
        log_fatal("Start MQTT subsciber worker thread failure\n");
        goto OUT;
    }
    log_nrml("Start MQTT subsciber worker thread ok\n");
    log_nrml("Start infrared monitor thread working...\n");
    while( ! g_signal.stop )
    {
        lux = tsl2561_get_lux();
        if( lux > hal_ctx->lux_threshold )
        {
            log_nrml("Lux[%.3f] > Treshold[%.3f], don't need auto light.\n", lux, hal_ctx->lux_threshold);
            sleep(30);
            continue;
        }
        log_nrml("start infrared monitor detect...\n");
        if( infrared_detect() )
        {
            log_nrml("Someone incoming detected by infrared\n");
            if( lux < hal_ctx->lux_threshold )
            {
                log_nrml("Lux[%.3f] < Treshold[%.3f], auto light on now..\n", lux, hal_ctx->lux_threshold);
                //turn_light(LIGHT_HALLWAY, ON);
                alarm(hal_ctx->gpio.light_intval);
            }
        }
    }
OUT:
    mosquitto_lib_cleanup();
    hal_term(hal_ctx);
    logger_term();
    return 0;
} /* ----- End of main() ----- */
int check_set_program_running(int daemon)
{
    if( check_daemon_running(DAEMON_PIDFILE) )
    {
        log_err("Program already running, process exit now");
        return -1;
    }
    if( daemon )
    {
        if( set_daemon_running(DAEMON_PIDFILE) < 0 )
        {
            log_err("set program running as daemon failure\n");
            return -2;
        }
    }
    else
    {
        if( record_daemon_pid(DAEMON_PIDFILE) < 0 )
        {
            log_err("record program running PID failure\n");
            return -3;
        }
    }
    return 0;
}
void sub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
{
    iotd_ctx_t             *ctx = (iotd_ctx_t *)userdata;
    if( result )
    {
        log_err("Subscriber connect to broker server failed, rv=%d\n", result);
        return ;
    }
    log_nrml("Subscriber connect to broker server[%s:%d] successfully\n", ctx->mqtt_ctx.host, ctx->mqtt_ctx.port);
    mosquitto_subscribe(mosq, NULL, ctx->mqtt_ctx.subTopic, ctx->mqtt_ctx.subQos);
}
void sub_disconnect_callback(struct mosquitto *mosq, void *userdata, int result)
{
    iotd_ctx_t             *ctx = (iotd_ctx_t *)userdata;
    log_warn("Subscriber disconnect to broker server[%s:%d], reason=%d\n",
            ctx->mqtt_ctx.host, ctx->mqtt_ctx.port, result);
}
void proc_json_items(cJSON *root)
{
    int                    i;
    char                  *value;
    cJSON                 *item;
    cJSON                 *array;
    if( !root )
    {
        log_err("Invalid input arguments $root\n");
        return ;
    }
    for( i=0; i<cJSON_GetArraySize(root); i++ )
    {
        item = cJSON_GetArrayItem(root, i);
        if( !item )
            break;
        /* if item is cJSON_Object, then recursive call proc_json */
        if( cJSON_Object == item->type )
        {
            proc_json_items(item);
        }
        else if( cJSON_Array != item->type )
        {
            value = cJSON_Print(item);
            /* light controled by relay */
            if( !strcasecmp(item->string, "hallway") )
            {
                if( strcasestr(value, "on") )
                {
                    //turn_light(LIGHT_HALLWAY, ON);
                    log_nrml("Turn on hallway light\n");
                }
                else if( strcasestr(value, "off") )
                {
                    //turn_light(LIGHT_HALLWAY, OFF);
                    log_nrml("Turn off hallway light\n");
                }
            }
            else if( !strcasecmp(item->string, "livroom_left") )
            {
                if( strcasestr(value, "on") )
                {
                    //turn_light(LIGHT_LVROOM_LEFT, ON);
                    log_nrml("Turn on livingroom left light\n");
                }
                else if( strcasestr(value, "off") )
                {
                    //turn_light(LIGHT_LVROOM_LEFT, OFF);
                    log_nrml("Turn off livingroom left light\n");
                }
            }
            if( !strcasecmp(item->string, "livroom_right") )
            {
                if( strcasestr(value, "on") )
                {
                    //turn_light(LIGHT_LVROOM_RIGHT, ON);
                    log_nrml("Turn on livingroom right light\n");
                }
                else if( strcasestr(value, "off") )
                {
                    //turn_light(LIGHT_LVROOM_RIGHT, OFF);
                    log_nrml("Turn off livingroom right light\n");
                }
            }
            free(value); /* must free it, or it will result memory leak */
        }
    }
}
void sub_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
    iotd_ctx_t             *ctx = (iotd_ctx_t *)userdata;
    cJSON                  *root = NULL;
    cJSON                  *item;
    char                   *value;
    if ( !message->payloadlen )
    {
        log_err("%s (null)\n", message->topic);
        return ;
    }
    log_dbg("Subscriber receive message: '%s'\n", message->payload);
    root = cJSON_Parse(message->payload);
    if( !root )
    {
        log_err("cJSON_Parse parser failure: %s\n", cJSON_GetErrorPtr());
        return ;
    }
    /* check ID matched or not */
    item = cJSON_GetObjectItem(root, "id");
    if( !item )
    {
        log_err("cJSON_Parse get ID failure: %s\n", cJSON_GetErrorPtr());
        goto OUT;
    }
    value = cJSON_PrintUnformatted(item);
    if( strcasecmp(value, ctx->mqtt_ctx.id) )
    {
        log_warn("cJSON_Parse get ID not matchs [%s<->%s], drop this message!\n", value, ctx->mqtt_ctx.id);
        free(value);
        goto OUT;
    }
    free(value);
    /* proc JSON mesage */
    proc_json_items(root);
OUT:
    cJSON_Delete(root); /* must delete it, or it will result memory leak */
    return ;
}
void *mqtt_sub_worker(void *args)
{
    struct mosquitto       *mosq;
    bool                    session = true;
    iotd_ctx_t             *ctx = (iotd_ctx_t *)args;
    mqtt_ctx_t             *mqtt_ctx;
    if( !ctx )
    {
        log_err("Invalid input arguments\n");
    return NULL;
    }
    mqtt_ctx = &ctx->mqtt_ctx;
    mosq = mosquitto_new(NULL, session, ctx);
    if( !mosq )
    {
        log_err("mosquitto_new failure\n");
        return NULL;
    }
    /* set connnect to broker username and password  */
    if( strlen(mqtt_ctx->uid)> 0  && strlen(mqtt_ctx->pwd)> 0 )
        mosquitto_username_pw_set(mosq, mqtt_ctx->uid, mqtt_ctx->pwd);
    /* set callback functions */
    mosquitto_connect_callback_set(mosq, sub_connect_callback);
    mosquitto_disconnect_callback_set(mosq, sub_disconnect_callback);
    mosquitto_message_callback_set(mosq, sub_message_callback);
    while( !g_signal.stop )
    {
        /* connect to MQTT broker  */
        if( mosquitto_connect(mosq, mqtt_ctx->host, mqtt_ctx->port, mqtt_ctx->keepalive) )
        {
            log_err("Subscriber connect to broker[%s:%d] failure: %s\n", mqtt_ctx->host, mqtt_ctx->port, strerror(errno));
            msleep(1000);
            continue;
        }
        /* -1: use default timeout 1000ms  1: unused */
        mosquitto_loop_forever(mosq, -1, 1);
    }
    mosquitto_destroy(mosq);
    return NULL;
}
iotd/makefile
New file
@@ -0,0 +1,74 @@
#*********************************************************************************
#      Copyright:  (C) 2012 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292>
#                  All rights reserved.
#
#       Filename:  Makefile
#    Description:  This Makefile used to compile all the C source code file in current
#                  folder to one excutable binary files.
#
#        Version:  1.0.0(10/08/2011~)
#                  Author:  Guo Wenxue <guowenxue@gmail.com>
#      ChangeLog:  1, Release initial version on "11/11/2011 01:29:33 PM"
#
#********************************************************************************/
PRJ_PATH=$(shell pwd)
INSTPATH=/usr/bin
IMAGE_NAME=$(shell basename ${PRJ_PATH})
#CROSSTOOL=
export CC=${CROSSTOOL}gcc
export CXX=${CROSSTOOL}g++
export AR=${CROSSTOOL}ar
export AS=${CROSSTOOL}as
export LD=${CROSSTOOL}ld
export RANLIB=${CROSSTOOL}ranlib
export STRIP=${CROSSTOOL}strip
export LDFLAGS
DIRS= conf hal lylib
DIRS_PATH=$(patsubst %,${PRJ_PATH}/%,$(DIRS))
CFLAGS=$(patsubst %,-I%,$(DIRS_PATH))
LDFLAGS=$(patsubst %,-L%,$(DIRS_PATH))
LIBS=$(patsubst %,-l%,$(DIRS))
CFLAGS+=-D_GNU_SOURCE
CFLAGS+=-I ${PRJ_PATH}
CFLAGS+=-I ${PRJ_PATH}/3rdlib/install/include/
LDFLAGS+=-L ${PRJ_PATH}/3rdlib/install/lib
LIBS+=-lgpiod -lmosquitto -lcjson -lpthread -lm
SRCFILES = $(wildcard *.c)
all: thirdlib entry modules binary
entry:
    @echo " ";
    @echo " =========================================================";
    @echo " **        Compile \"${BINARIES}\" for ${ARCH}         ";
    @echo " =========================================================";
thirdlib:
    @make CROSSTOOL=${CROSSTOOL} -C 3rdlib
modules:
    set -e; for d in ${DIRS}; do $(MAKE) CROSSTOOL=${CROSSTOOL} CFLAGS="${CFLAGS}" -C $${d}; done
binary:  ${SRCFILES}
    $(CC) $(CFLAGS) -o ${IMAGE_NAME} $^ ${LDFLAGS} ${LIBS}
    @echo " Compile over"
install:
    @cp $(IMAGE_NAME) ${INSTPATH}
clean:
    set -e; for d in ${DIRS}; do $(MAKE) clean -C $${d}; done
    @rm -f *.o $(IMAGE_NAME)
distclean: clean
    @rm -f  tags cscope*
    @make distclean -C 3rdlib
.PHONY: clean entry