Add iotd program framework source code
 
	
	
	
	
	
	
	
	
	
	
	
	
	
| New file | 
 |  |  | 
 |  |  | #!/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 | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | all: | 
 |  |  |     sed -i -e "s|^CROSSTOOL=.*|CROSSTOOL=${CROSSTOOL}|g" build.sh | 
 |  |  |     @bash build.sh | 
 |  |  |  | 
 |  |  | distclean:  | 
 |  |  |     @bash build.sh clean | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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; | 
 |  |  | } | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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_  ----- */ | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | 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 | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | [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 | 
 |  |  |  | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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; | 
 |  |  | } | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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 | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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 | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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_  ----- */ | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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 ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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_  ----- */ | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | 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 | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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 | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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 | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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], ®_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; | 
 |  |  | } | 
 |  |  |  | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /******************************************************************************** | 
 |  |  |  *      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_  ----- */ | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @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 */ | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @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 | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @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 */ | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @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 | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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 | 
 |  |  |  | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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); | 
 |  |  |  | 
 |  |  |     } | 
 |  |  | } | 
 
| New file | 
 |  |  | 
 |  |  | /******************************************************************************** | 
 |  |  |  *      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_  ----- */ | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | 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 | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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); | 
 |  |  | } | 
 |  |  |  | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /******************************************************************************** | 
 |  |  |  *      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 | 
 
| New file | 
 |  |  | 
 |  |  | /******************************************************************************** | 
 |  |  |  *      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 | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      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; | 
 |  |  | } | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | #********************************************************************************* | 
 |  |  | #      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 | 
 |  |  |  |