From 542454b6e29fd5359fca69b71e1666ad71a7b7d7 Mon Sep 17 00:00:00 2001 From: guowenxue <guowenxue@gmail.com> Date: Wed, 30 Jan 2019 00:57:22 +0800 Subject: [PATCH] Add monitred and mqttd source code --- monitord/cp_logger.h | 111 + monitord/cp_comport.c | 600 ++++++++++ monitord/cp_logger.c | 433 +++++++ mqttd/sht20.h | 74 + monitord/cp_comport.h | 67 + monitord/cp_proc.c | 342 +++++ mqttd/led.c | 60 + monitord/beep.h | 38 monitord/cp_proc.h | 42 monitord/makefile | 68 + monitord/beep.c | 216 +++ mqttd/makefile | 68 + mqttd/sht20.c | 439 +++++++ mqttd/led.h | 48 monitord/cp_socket.c | 357 ++++++ mqttd/ds18b20.c | 105 + mqttd/ds18b20.h | 19 monitord/monitord.c | 186 +++ monitord/monitord.h | 18 monitord/cp_socket.h | 65 + mqttd/main.c | 92 + 21 files changed, 3,448 insertions(+), 0 deletions(-) diff --git a/monitord/beep.c b/monitord/beep.c new file mode 100644 index 0000000..b02aa61 --- /dev/null +++ b/monitord/beep.c @@ -0,0 +1,216 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT System Studio + * All rights reserved. + * + * Filename: led.c + * Description: This file is used to control Passive buzzer or Active buzzer + * + * pi@raspberrypi:~ $ gpio readall show BCM and wPi pinmap + * + * VCC ---- 5V/3.3V + * GND ---- GND + * I/O ---- GPIO.18 ---- GPIO.1 + * BCM wPi + * + * 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 <wiringPi.h> +#include "beep.h" + +//#define CONFIG_ACTV_BEEP + +/* Only passive buzzer can play tone */ +#ifndef CONFIG_ACTV_BEEP +#define CONFIG_PLAY_LITTLE_STAR +//#define CONFIG_PLAY_TONE_TEST +#endif + +void play_tone_freq(void); +void play_little_star(void); + + +/*+-----------------------------+ + *| Turn buzzer on or off API | + *+-----------------------------+*/ +int turn_passive_beep(int cmd, int freq) +{ + int range; + + if(OFF == cmd) + { + pwmWrite(PWM_PIN, 0); + pinMode(PWM_PIN, INPUT) ; + } + else + { + + if(freq<2000 || freq>5000) + { + printf("Beep set invalid PWM frequency[%d]!\n", freq); + return -1; + } + + /* Set GPIO as PWM output mode */ + pinMode(PWM_PIN, PWM_OUTPUT) ; + + /* Set PWM mode as ms mode */ + pwmSetMode(PWM_MODE_MS); + + /* Set PWM clock: 19.2MHz/32=600KHz */ + pwmSetClock(32); + + /* Set PWM frequency */ + range=600000/freq; + pwmSetRange(range); + + /* Set PWM duty 50% */ + pwmWrite(PWM_PIN, range/2); + } +} + + +/* Turn ON/OFF buzzer, Active Buzzer can not set frequency */ +int turn_active_beep(int cmd) +{ + + /* Active buzzer VCC connect to: + 5V: Both high and low level will be on + 3.3V: Low level be on and High level be off + + So we use INPUT or OUTPUT to control the Beeper + */ + if(OFF == cmd) + { + pinMode(PWM_PIN, INPUT); + } + else + { + pinMode(PWM_PIN, OUTPUT); + digitalWrite(PWM_PIN, LOW); + } + + return 0; +} + + +/*+---------------------------------+ + *| Play Tone OD,RE,MI....XI,DO1 | + *+---------------------------------+*/ + +enum +{ + UNUSED=0, + DO, + RE, + MI, + FA, + SO, + LA, + XI, + DO1, + RI1, + TONE_MAX, +}; + +#define msleep(x) usleep( 1000*(x) ) + +static int tone_freq[TONE_MAX]={0, 2093, 2349, 2637, 2794, 3136, 3520, 3952, 4186, 4698 }; +//static int tone_freq[TONE_MAX]={0, 2000, 2130, 2250, 2360, 2450, 2530, 2620, 2700, 2780}; + +static inline void play_tone(int tone, int delay) +{ + if(tone<DO || tone>RI1) + return ; + + turn_passive_beep(ON, tone_freq[tone]); + msleep(delay); + turn_passive_beep(OFF, 0); +} + +void play_tone_freq(void) +{ + int i; + + for(i=DO; i<TONE_MAX; i++) + { + play_tone( i, 500 ); + msleep(500); + } +} + + +/*+------------------------------+ + *| Play song "Little Start" | + *+------------------------------+*/ + +typedef struct tone_s +{ + int tone; + int delay_ms; + +} tone_t; + +#define DEF_DELAY 350 +static tone_t little_start_notation[]= +{ + {DO, DEF_DELAY}, + {DO, DEF_DELAY}, + {SO, DEF_DELAY}, + {SO, DEF_DELAY}, + {LA, DEF_DELAY}, + {LA, DEF_DELAY}, + {SO, DEF_DELAY*2}, + + {FA, DEF_DELAY}, + {FA, DEF_DELAY}, + {MI, DEF_DELAY}, + {MI, DEF_DELAY}, + {RE, DEF_DELAY}, + {RE, DEF_DELAY}, + {DO, DEF_DELAY*2}, + + + {SO, DEF_DELAY}, + {SO, DEF_DELAY}, + {FA, DEF_DELAY}, + {FA, DEF_DELAY}, + {MI, DEF_DELAY}, + {MI, DEF_DELAY}, + {RE, DEF_DELAY*2}, + + + {SO, DEF_DELAY}, + {SO, DEF_DELAY}, + {FA, DEF_DELAY}, + {FA, DEF_DELAY}, + {MI, DEF_DELAY}, + {MI, DEF_DELAY}, + {RE, DEF_DELAY*2}, +}; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +void play_little_star(void) +{ + int i; + + for(i=0; i<ARRAY_SIZE(little_start_notation); i++) + { + play_tone(little_start_notation[i].tone, little_start_notation[i].delay_ms); + msleep(30); + } +} + + diff --git a/monitord/beep.h b/monitord/beep.h new file mode 100644 index 0000000..68c4751 --- /dev/null +++ b/monitord/beep.h @@ -0,0 +1,38 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT System Studio + * All rights reserved. + * + * Filename: beep.h + * Description: This file is used to control Passive buzzer or Active buzzer + * + * pi@raspberrypi:~ $ gpio readall show BCM and wPi pinmap + * + * VCC ---- 5V/3.3V + * GND ---- GND + * I/O ---- GPIO.18 ---- GPIO.1 + * BCM wPi + * + * 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 __BEEP_H +#define __BEEP_H + +#define OFF 0 +#define ON 1 + +#define BEEP_FREQ 2400 + +/* Use Pin12 == GPIO18(BCM) == GPIO.1(wPi) */ +#define PWM_PIN 1 + + +int turn_passive_beep(int cmd, int freq); + +int turn_active_beep(int cmd); + +#endif + diff --git a/monitord/cp_comport.c b/monitord/cp_comport.c new file mode 100644 index 0000000..8363cb2 --- /dev/null +++ b/monitord/cp_comport.c @@ -0,0 +1,600 @@ +/* ******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_comport.c + * Description: It's the comport operate library. + * + * Version: 1.0.0(10/17/2011~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "10/17/2011 03:33:25 PM" + * + ********************************************************************************/ + +#include "cp_comport.h" + +/************************************************************************************** + * Description: Set the comport structure + * Input Args: dev_name: The comport device name path, such as '/dev/ttyS3' + * baudrate: The baudrate, such as 115200 + * settings: The databit,parity,stopbit,flowctrl settings, such as '8N1N' + * Output Args: NONE + * Return Value: The cp_comport_t structure pointer. + *************************************************************************************/ +cp_comport_t *comport_init(const char *dev_name, int baudrate, const char *settings) +{ + cp_comport_t *comport = NULL; + if (NULL == (comport = (cp_comport_t *) malloc(sizeof(cp_comport_t)))) + { + return NULL; + } + memset(comport, 0, sizeof(cp_comport_t)); + comport->connected = 0; + comport->frag_size = 128; + + strncpy(comport->dev_name, dev_name, DEVNAME_LEN); + comport->baudrate = baudrate; + + set_settings(comport, settings); +#ifdef COM_DEBUG + disp_settings(comport); +#endif + + return comport; +} + +#ifdef COM_DEBUG +void disp_settings(cp_comport_t * comport) +{ + COM_PRINT("Device:\t\t\t\"%s\"\n", comport->dev_name); + COM_PRINT("Baudrate:\t\t%ld\n", comport->baudrate); + COM_PRINT("DataBit:\t\t\'%d\'\n", comport->databit); + switch (comport->parity) + { + case 0: + COM_PRINT("Parity:\t\t\t\'N\'\n"); + break; + case 1: + COM_PRINT("Parity:\t\t\t\'O\'\n"); + break; + case 2: + COM_PRINT("Parity:\t\t\t\'E\'\n"); + break; + case 3: + COM_PRINT("Parity:\t\t\t\'S\'\n"); + break; + } + COM_PRINT("StopBit:\t\t\'%ld\'\n", (long int)comport->stopbit); + switch (comport->flowctrl) + { + case 0: + COM_PRINT("FlowCtrl:\t\t\'N\'\n"); + break; + case 1: + COM_PRINT("FlowCtrl:\t\t\'S\'\n"); + break; + case 2: + COM_PRINT("FlowCtrl:\t\t\'H\'\n"); + break; + case 3: + COM_PRINT("FlowCtrl:\t\t\'B\'\n"); + break; + } + COM_PRINT("\n"); + return; +} +#endif + +/************************************************************************************** + * Description: Set the comport databit,parity,stopbit,flowctrl + * Input Args: comport: the cp_comport_t pointer + * settings: The databit/parity/stopbit/flowctrl settings as like "8N1N" + * Output Args: NONE + * Return Value: NONE + *************************************************************************************/ +void set_settings(cp_comport_t * comport, const char *settings) +{ + if(NULL==settings || NULL==comport) + return ; + + switch (settings[0]) /* data bit */ + { + case '7': + comport->databit = 7; + break; + case '8': + default: + comport->databit = 8; + break; + } + + switch (settings[1]) /* parity */ + { + case 'O': + case 'o': + comport->parity = 1; + break; + case 'E': + case 'e': + comport->parity = 2; + break; + case 'S': + case 's': + comport->parity = 3; + break; + case 'N': + case 'n': + default: + comport->parity = 0; + break; + } + + switch (settings[2]) /* stop bit */ + { + case '0': + comport->stopbit = 0; + break; + case '1': + default: + comport->stopbit = 1; + break; + } + + switch (settings[3]) /* flow control */ + { + case 'S': + case 's': + comport->flowctrl = 1; + break; + case 'H': + case 'h': + comport->flowctrl = 2; + break; + case 'B': + case 'b': + comport->flowctrl = 3; + break; + case 'N': + case 'n': + default: + comport->flowctrl = 0; + break; + } +} + +void comport_close(cp_comport_t * comport) +{ + if (0 != comport->fd) + { + COM_PRINT("Close device \"%s\"\n", comport->dev_name); + close(comport->fd); + } + comport->connected = 0x00; + comport->fd = -1; +} + +void cp_comport_term(cp_comport_t * comport) +{ + if(NULL == comport) + return; + + if (0 != comport->fd) + { + comport_close(comport); + } + memset(comport, 0x00, sizeof(cp_comport_t)); + free(comport); + comport = NULL; + + return; +} + +int comport_open(cp_comport_t * comport) +{ + int retval = -1; + struct termios old_cfg, new_cfg; + int old_flags; + long tmp; + + if(NULL==comport) + return -1; + + comport_close(comport); + + + /* Not a TTY device */ + if( !strstr(comport->dev_name, "tty")) + { + COM_PRINT("Open Not tty device \"%s\"\n", comport->dev_name); + comport->fd = open(comport->dev_name, O_RDWR); + retval = comport->fd<0 ? -2 : comport->fd; + goto CleanUp; + } + + comport->fd = open(comport->dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (comport->fd < 0) + { + retval = -3; + goto CleanUp; + } + COM_PRINT("Open device \"%s\"\n", comport->dev_name); + + if ((-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) + && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK))) + { + // Flush input and output + if (-1 == tcflush(comport->fd, TCIOFLUSH)) + { + retval = -4; + goto CleanUp; + } + } + else // Failure + { + retval = -5; + goto CleanUp; + } + + if (0 != tcgetattr(comport->fd, &old_cfg)) + { + retval = -6; // Failed to get Com settings + goto CleanUp; + } + + memset(&new_cfg, 0, sizeof(new_cfg)); + + /*=====================================*/ + /* Configure comport */ + /*=====================================*/ + + new_cfg.c_cflag &= ~CSIZE; + new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + new_cfg.c_oflag &= ~(OPOST); + + /* Set the data bit */ + switch (comport->databit) + { + case 0x07: + new_cfg.c_cflag |= CS7; + break; + case 0x06: + new_cfg.c_cflag |= CS6; + break; + case 0x05: + new_cfg.c_cflag |= CS5; + break; + default: + new_cfg.c_cflag |= CS8; + break; + } + + /* Set the parity */ + switch (comport->parity) + { + case 0x01: // Odd + new_cfg.c_cflag |= (PARENB | PARODD); + new_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0x02: // Even + new_cfg.c_cflag |= PARENB; + new_cfg.c_cflag &= ~PARODD;; + new_cfg.c_cflag |= (INPCK | ISTRIP); + break; + case 0x03: + new_cfg.c_cflag &= ~PARENB; + new_cfg.c_cflag &= ~CSTOPB; + break; + default: + new_cfg.c_cflag &= ~PARENB; + } + + /* Set Stop bit */ + if (0x01 != comport->stopbit) + { + new_cfg.c_cflag |= CSTOPB; + } + else + { + new_cfg.c_cflag &= ~CSTOPB; + } + + /* Set flow control */ + switch (comport->flowctrl) + { + case 1: // Software control + case 3: + new_cfg.c_cflag &= ~(CRTSCTS); + new_cfg.c_iflag |= (IXON | IXOFF); + break; + case 2: // Hardware control + new_cfg.c_cflag |= CRTSCTS; // Also called CRTSCTS + new_cfg.c_iflag &= ~(IXON | IXOFF); + break; + default: // NONE + new_cfg.c_cflag &= ~(CRTSCTS); + new_cfg.c_iflag &= ~(IXON | IXOFF); + break; + } + + /* Set baudrate */ + switch (comport->baudrate) + { + case 115200: + tmp = B115200; + break; + case 57600: + tmp = B57600; + break; + case 38400: + tmp = B38400; + break; + case 19200: + tmp = B19200; + break; + case 9600: + tmp = B9600; + break; + case 4800: + tmp = B4800; + break; + case 2400: + tmp = B2400; + break; + case 1800: + tmp = B1800; + break; + case 1200: + tmp = B1200; + break; + case 600: + tmp = B600; + break; + case 300: + tmp = B300; + break; + case 200: + tmp = B200; + break; + case 150: + tmp = B150; + break; + case 134: + tmp = B134; + break; + case 110: + tmp = B110; + break; + case 75: + tmp = B75; + break; + case 50: + tmp = B50; + break; + default: + tmp = B115200; + } + cfsetispeed(&new_cfg, tmp); + cfsetispeed(&new_cfg, tmp); + + /* Set the Com port timeout settings */ + new_cfg.c_cc[VMIN] = 0; + new_cfg.c_cc[VTIME] = 0; + + tcflush(comport->fd, TCIFLUSH); + if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg)) + { + retval = -7; // Failed to set device com port settings + goto CleanUp; + } + + COM_PRINT("Connected device \"%s\".\n", comport->dev_name); + comport->connected = 0x01; + retval = comport->fd; + +CleanUp: + COM_PRINT("Open device \"%s\" %s.\n", comport->dev_name, retval>0 ? "successfully" : "failure"); + return retval; +} + +void nonblock() +{ + struct termios ttystate; + + //get the terminal state + tcgetattr(STDIN_FILENO, &ttystate); + + //turn off canonical mode + ttystate.c_lflag &= ~ICANON; + //minimum of number input read. + ttystate.c_cc[VMIN] = 1; + + //set the terminal attributes. + tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); +} + +int kbhit() +{ + struct timeval tv; + fd_set fds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 + select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); + return FD_ISSET(STDIN_FILENO, &fds); +} + +int comport_recv(cp_comport_t * comport, char *buf, int buf_size, unsigned long timeout) +{ + int retval = 0; // Function return value + int iRet; + fd_set stReadFds, stExcpFds; + struct timeval stTime; + + if (NULL == buf || 0 >= buf_size) + { + COM_PRINT("%s() usage error.\n", __FUNCTION__); + retval = -1; + goto CleanUp; + } + + if (0x01 != comport->connected) + { + COM_PRINT("%s() comport not connected.\n", __FUNCTION__); + retval = -2; + goto CleanUp; + } + + //printf("bufsize=%d timeout=%lu\n", buf_size, timeout); + + FD_ZERO(&stReadFds); + FD_ZERO(&stExcpFds); + FD_SET(comport->fd, &stReadFds); + FD_SET(comport->fd, &stExcpFds); + + if (0xFFFFFFFF != timeout) + { + stTime.tv_sec = (time_t) (timeout / 1000); + stTime.tv_usec = (long)(1000 * (timeout % 1000)); + + iRet = select(comport->fd + 1, &stReadFds, 0, &stExcpFds, &stTime); + if (0 == iRet) + { + retval = 0; // No data in Com port buffer + goto CleanUp; + } + else if (0 < iRet) + { + if (0 != FD_ISSET(comport->fd, &stExcpFds)) + { + retval = -6; // Error during checking recv status + COM_PRINT("Error checking recv status.\n"); + goto CleanUp; + } + + if (0 == FD_ISSET(comport->fd, &stReadFds)) + { + retval = 0; // No incoming data + COM_PRINT("No incoming data.\n"); + goto CleanUp; + } + } + else + { + if (EINTR == errno) + { + COM_PRINT("catch interrupt signal.\n"); + retval = 0; // Interrupted signal catched + } + else + { + COM_PRINT("Check recv status failure.\n"); + retval = -7; // Error during checking recv status + } + + goto CleanUp; + } + } + + usleep(10000); /* sleep for 10ms for data incoming */ + + // Get data from Com port + iRet = read(comport->fd, buf, buf_size); + if (0 > iRet) + { + if (EINTR == errno) + retval = 0; // Interrupted signal catched + else + retval = -3; // Failed to read Com port + + goto CleanUp; + } + +#if 0 + { + int i=0; + printf("Receive %d bytes data: \n", iRet); + for(i=0; i<iRet; i++) + { + printf("0x%02x ", buf[i]); + } + printf("\n"); + } +#endif + + retval = iRet; + + CleanUp: + return retval; + +} + +int comport_send(cp_comport_t * comport, char *buf, int send_bytes) +{ + char *ptr, *end; + int retval = 0; + int send = 0; + + if (NULL == buf || 0 >= send_bytes) + { + COM_PRINT("%s() Usage error.\n", __FUNCTION__); + retval = -1; + goto CleanUp; + } + + if (0x01 != comport->connected) // Comport not opened ? + { + retval = -3; + COM_PRINT("Serail not connected.\n"); + goto CleanUp; + } + + //printf("Send %s with %d bytes.\n", buf, send_bytes); + + // Large data, then slice them and send + if (comport->frag_size < send_bytes) + { + ptr = buf; + end = buf + send_bytes; + + do + { + // Large than frag_size + if (comport->frag_size < (end - ptr)) + { + send = write(comport->fd, ptr, comport->frag_size); + if (0 >= send || comport->frag_size != send) + { + retval = -4; + goto CleanUp; + } + ptr += comport->frag_size; + } + else // Less than frag_size, maybe last fragmention. + { + send = write(comport->fd, ptr, (end - ptr)); + if (0 >= send || (end - ptr) != send) + { + retval = -4; + goto CleanUp; + } + ptr += (end - ptr); + } + } + while (ptr < end); + } + else // The send data is not large than a fragmention. + { + send = write(comport->fd, buf, send_bytes); + if (0 >= send || send_bytes != send) + { + retval = -5; + goto CleanUp; + } + } + + CleanUp: + return retval; +} + diff --git a/monitord/cp_comport.h b/monitord/cp_comport.h new file mode 100644 index 0000000..5c08b9b --- /dev/null +++ b/monitord/cp_comport.h @@ -0,0 +1,67 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_comport.h + * Description: This head file is for the common TTY/Serial port operator library + * + * Version: 1.0.0(10/17/2011~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "10/17/2011 03:33:25 PM" + * + ********************************************************************************/ +#ifndef _CP_COMPORT_H +#define _CP_COMPORT_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> + +#define BUF_64 64 + +#ifndef DEVNAME_LEN +#define DEVNAME_LEN 64 +#endif + +//#define COM_DEBUG +#ifdef COM_DEBUG +#define COM_PRINT(format,args...) printf(format, ##args) +#else +#define COM_PRINT(format,args...) do{} while(0); +#endif + +//#define msleep(m) {struct timespec cSleep; cSleep.tv_sec = 0; cSleep.tv_nsec = m * 1000; nanosleep(&cSleep, 0);} + +typedef struct cp_comport_s +{ + unsigned char databit, parity, stopbit, flowctrl, connected; + char dev_name[DEVNAME_LEN]; + unsigned char used; /* This comport used or not now */ + int fd; + int frag_size; + long baudrate; +} cp_comport_t; + +cp_comport_t *comport_init(const char *dev_name, int baudrate, const char *settings); +void comport_close(cp_comport_t * comport); +int comport_open(cp_comport_t * comport); +void cp_comport_term(cp_comport_t * comport); +int comport_recv(cp_comport_t * comport, char *buf, int buf_size, unsigned long timeout); +int comport_send(cp_comport_t * comport, char *buf, int send_bytes); + +void set_settings(cp_comport_t * comport, const char *settings); +void disp_settings(cp_comport_t * comport); +void nonblock(); +int kbhit(); + +#endif diff --git a/monitord/cp_logger.c b/monitord/cp_logger.c new file mode 100644 index 0000000..efb04e5 --- /dev/null +++ b/monitord/cp_logger.c @@ -0,0 +1,433 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_log.c + * Description: This file is the linux infrastructural logger system library + * + * Version: 1.0.0(08/08/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "08/08/2012 04:24:01 PM" + * + ********************************************************************************/ + +#include "cp_logger.h" +//#include "cp_common.h" + +#define PRECISE_TIME_FACTOR 1000 + +static unsigned long log_rollback_size = LOG_ROLLBACK_NONE; + +static cp_logger *logger = NULL; + +char *log_str[LOG_LEVEL_MAX + 1] = { "", "F", "E", "W", "N", "D", "I", "T", "M" }; + +static char *cp_time_format = DEFAULT_TIME_FORMAT; + +void cp_log_set_time_format(char *time_format) +{ + cp_time_format = time_format; +} + +static void cp_log_default_signal_handler(int sig) +{ + if(!logger) + return ; + + if (sig == SIGHUP) + { + signal(SIGHUP, cp_log_default_signal_handler); + log_fatal("SIGHUP received - reopenning log file [%s]", logger->file); + cp_log_reopen(); + } +} + +static void log_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] [PID/TID] [File/Line] [Content]\n"); +#else + fprintf(logger->fp, " [Date] [Time] [Level] [PID/TID] [Content]\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); + log_banner("Already rollback"); + } + } +} + +cp_logger *cp_log_init(cp_logger *log, char *filename, int level, int log_size) +{ + if(NULL == log) + { + logger = malloc(sizeof(cp_logger)); + memset(logger, 0, sizeof(cp_logger)); + logger->flag |= CP_LOGGER_MALLOC; + } + else + { + logger = log; + logger->flag |= CP_LOGGER_ARGUMENT; + } + + if(NULL == logger) + { + return NULL; + } + + strncpy(logger->file, filename, FILENAME_LEN); + logger->level = level; + logger->size = log_size; + + return logger; +} + +int cp_log_open(void) +{ + struct sigaction act; + char *filemode; + + if(!logger) + { + return -1; + } + + log_rollback_size = logger->size <= 0 ? LOG_ROLLBACK_NONE : logger->size*1024; /* Unit KiB */ + + if ('\0' == logger->file) + return -1; + + if (!strcmp(logger->file, DBG_LOG_FILE)) + { + logger->fp = stderr; + log_rollback_size = LOG_ROLLBACK_NONE; + logger->flag |= CP_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\n", logger->file, filemode); + return -2; + } + + act.sa_handler = cp_log_default_signal_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGHUP, &act, NULL); + + OUT: + log_banner("Initialize"); + + return 0; +} + +void cp_log_close(void) +{ + if (!logger || !logger->fp ) + return; + + log_banner("\nTerminate"); + cp_log_raw("\n\n\n\n"); + + fflush(logger->fp); + + fclose(logger->fp); + logger->fp = NULL; + + return ; +} + +int cp_log_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 & CP_LOGGER_CONSOLE ) + { + fflush(logger->fp); + logger->fp = stderr; + return 0; + } + + if (logger->fp) + { + cp_log_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) + { + log_banner("\nReopen"); + } + return rc; +} + +void cp_log_term(void) +{ + if(!logger) + return ; + + cp_log_close(); + + if (logger->flag & CP_LOGGER_MALLOC ) + { + free(logger); + } + logger = NULL; +} + +void cp_log_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 ; + + pthread_t tid; + + check_and_rollback(); + +#ifdef MULTHREADS + tid = pthread_self(); +#else + tid = getpid(); +#endif + + gettimeofday(&now, NULL); + local = localtime(&now.tv_sec); + + strftime(timestr, 256, cp_time_format, local); + vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp); + +#ifdef DUMPLICATE_OUTPUT + printf("%s.%03ld [%s] [%06lu]: %s", + timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, buf); +#endif + + if (logger->fp) + fprintf(logger->fp, "%s.%03ld [%s] [%06lu]: %s", timestr, now.tv_usec / PRECISE_TIME_FACTOR, + level, tid, 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 ; + + pthread_t tid; + + check_and_rollback(); + +#ifdef MULTHREADS + tid = pthread_self(); +#else + tid = getpid(); +#endif + + gettimeofday(&now, NULL); + local = localtime(&now.tv_sec); + + strftime(timestr, 256, cp_time_format, local); + vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp); + +#ifdef DUMPLICATE_OUTPUT + printf("%s.%03ld [%s] [%06lu] (%s [%04d]) : %s", + timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, file, line, buf); +#endif + + if (logger->fp) + fprintf(logger->fp, "%s.%03ld [%s] [%06lu] (%s [%04d]) : %s", + timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, file, line, buf); + + if (logger->fp) + fflush(logger->fp); +} + +void cp_log_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 cp_log(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 cp_log_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 cp_log_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, " ", LINELEN); + +#ifdef DUMPLICATE_OUTPUT + printf("%s %s\n", prn, lit); +#endif + if (logger->fp) + fprintf(logger->fp, "%s %s\n", prn, lit); + + } +} diff --git a/monitord/cp_logger.h b/monitord/cp_logger.h new file mode 100644 index 0000000..7548514 --- /dev/null +++ b/monitord/cp_logger.h @@ -0,0 +1,111 @@ +/******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue <guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_logger.h + * Description: This file is the linux infrastructural logger system library + * + * Version: 1.0.0(08/08/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "08/08/2012 05:16:56 PM" + * + ********************************************************************************/ + +#ifndef __CP_LOG_H +#define __CP_LOG_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 <pthread.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 DEFAULT_LOGFILE "cp_logger.log" +#define DBG_LOG_FILE "console" /* 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 */ +#define LOG_FILE_LINE /* Log the file and line */ + +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, +}; + +#define CP_LOGGER_MALLOC 1<<0 +#define CP_LOGGER_ARGUMENT 0<<0 + +#define CP_LOGGER_CONSOLE 1<<1 +#define CP_LOGGER_FILE 0<<1 + +#define CP_LOGGER_LEVEL_OPT 1<<2 /* The log level is sepcified by the command option */ +typedef struct _cp_logger +{ + unsigned char flag; /* This logger pointer is malloc() or passed by argument */ + char file[FILENAME_LEN]; + int level; + int size; + + FILE *fp; +} cp_logger; + +extern char *log_str[]; + +extern cp_logger *cp_log_init(cp_logger *log, char *filename, int level, int log_size); +extern int cp_log_open(void); +extern void cp_log_set_time_format(char *time_format); +extern int cp_log_reopen(void); +extern void cp_log_term(void); +extern void cp_log_raw(const char *fmt, ...); +extern void cp_log(int level, char *fmt, ...); +extern void cp_log_line(int level, char *file, int line, char *fmt, ...); +extern void cp_log_str(int level, const char *msg); + +extern void cp_log_dump(int level, char *buf, int len); + +#ifdef LOG_FILE_LINE +#define log_trace(fmt, ...) cp_log_line(LOG_LEVEL_TRACE, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_info(fmt, ...) cp_log_line(LOG_LEVEL_INFO, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_dbg(fmt, ...) cp_log_line(LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_nrml(fmt, ...) cp_log_line(LOG_LEVEL_NRML, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_warn(fmt, ...) cp_log_line(LOG_LEVEL_WARN, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_err(fmt, ...) cp_log_line(LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define log_fatal(fmt, ...) cp_log_line(LOG_LEVEL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#else +#define log_trace(fmt, ...) cp_log(LOG_LEVEL_TRACE, fmt, ##__VA_ARGS__) +#define log_info(fmt, ...) cp_log(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__) +#define log_dbg(fmt, ...) cp_log(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__) +#define log_nrml(fmt, ...) cp_log(LOG_LEVEL_NRML, fmt, ##__VA_ARGS__) +#define log_warn(fmt, ...) cp_log(LOG_LEVEL_WARN, fmt, ##__VA_ARGS__) +#define log_err(fmt, ...) cp_log(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__) +#define log_fatal(fmt, ...) cp_log(LOG_LEVEL_FATAL, fmt, ##__VA_ARGS__) +#endif + + +#endif /* __CP_LOG_H */ diff --git a/monitord/cp_proc.c b/monitord/cp_proc.c new file mode 100644 index 0000000..769ec5c --- /dev/null +++ b/monitord/cp_proc.c @@ -0,0 +1,342 @@ +/********************************************************************************* + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_proc.c + * Description: This file is the process API + * + * Version: 1.0.0(11/06/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/06/2012 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 "cp_proc.h" +#include "cp_logger.h" + +CP_PROC_SIG g_cp_signal={0}; + +void cp_proc_sighandler(int sig) +{ + switch(sig) + { + case SIGINT: + log_warn("SIGINT - stopping\n"); + g_cp_signal.stop = 1; + break; + + case SIGTERM: + log_warn("SIGTERM - stopping\n"); + g_cp_signal.stop = 1; + break; + case SIGSEGV: + log_warn("SIGSEGV - stopping\n"); +#if 0 + if(g_cp_signal.stop) + exit(0); + + g_cp_signal.stop = 1; +#endif + break; + + case SIGPIPE: + log_warn("SIGPIPE - warnning\n"); + break; + + default: + break; + } +} + + +void cp_install_proc_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 = cp_proc_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: 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; +} + + +int thread_start(pthread_t * thread_id, THREAD_BODY * 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; +} + + +void exec_system_cmd(const char *format, ...) +{ + char cmd[256]; + va_list args; + //int done = 0; + + memset(cmd, 0, sizeof(cmd)); + + va_start(args, format); + //done = vsnprintf(cmd, sizeof(cmd), format, args); + vsnprintf(cmd, sizeof(cmd), format, args); + va_end(args); + + system(cmd); +} + + diff --git a/monitord/cp_proc.h b/monitord/cp_proc.h new file mode 100644 index 0000000..f79abe8 --- /dev/null +++ b/monitord/cp_proc.h @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_proc.h + * Description: This head file is for Linux process API + * + * Version: 1.0.0(11/06/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/06/2012 09:21:33 PM" + * + ********************************************************************************/ + +#ifndef __CP_PROC_H +#define __CP_PROC_H + +#include <signal.h> + +#define PID_ASCII_SIZE 11 + +typedef struct __CP_PROC_SIG +{ + int signal; + unsigned stop; /* 0: Not term 1: Stop */ +} CP_PROC_SIG; + +typedef void *(THREAD_BODY) (void *thread_arg); + +extern CP_PROC_SIG g_cp_signal; +extern void cp_install_proc_signal(void); + +extern void daemonize(int nochdir, int noclose); +extern int record_daemon_pid(const char *pid_file); +extern pid_t get_daemon_pid(const char *pid_file); +extern int check_daemon_running(const char *pid_file); +extern int set_daemon_running(const char *pid_file); + +extern void exec_system_cmd(const char *format, ...); + +extern int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg); + +#endif diff --git a/monitord/cp_socket.c b/monitord/cp_socket.c new file mode 100644 index 0000000..7773281 --- /dev/null +++ b/monitord/cp_socket.c @@ -0,0 +1,357 @@ +/********************************************************************************* + * Copyright: (C) 2014 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_socket.c + * Description: This file + * + * Version: 1.0.0(11/18/2014) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/18/2014 11:15:04 PM" + * + ********************************************************************************/ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <netdb.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/un.h> +#include <sys/types.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "cp_logger.h" +#include "cp_socket.h" + +int cp_sock_block_connect(cp_socket_t *sock, char *servaddr, unsigned short servport) +{ + int rv = -1; + int sock_fd = -1; + char service[20]; + struct addrinfo hints, *rp; + struct addrinfo *res = NULL; + struct in_addr inaddr; + char ipstr[20]; + int len; + + if(!sock || !servaddr || !servport ) + { + log_err("Invalid input argument\n"); + return -1; + } + + if(SOCK_STAT_CONNECTED == sock->status) + { + return 0; + } + + len = strlen(servaddr); + len = len>DOMAIN_MAX_LEN ? DOMAIN_MAX_LEN : len; + + memcpy(sock->servaddr, servaddr, strlen(servaddr)); + sock->servport = servport; + + log_nrml("Start socket connect to [%s:%d]...\n", sock->servaddr, sock->servport); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; /* Only support IPv4 */ + hints.ai_socktype = SOCK_STREAM; + + /* If Hostname is a valid IP address, then don't use name resolution */ + if( inet_aton(sock->servaddr, &inaddr) ) + { + log_info("%s is a valid IP address, don't use name resolution.\n", sock->servaddr); + hints.ai_flags |= AI_NUMERICHOST; + } + + /* Obtain address(es) matching servaddr/servport */ + snprintf(service, sizeof(service), "%d", sock->servport); + if( (rv=getaddrinfo(sock->servaddr, service, &hints, &res)) ) + { + log_err("getaddrinfo() parser [%s:%s] failed: %s\n", sock->servaddr, service, gai_strerror(rv)); + rv = -2; + goto out; + } + + /* getaddrinfo() returns a list of address structures. Try each address until connected */ + rv = -3; /* default return value set be connect failure */ + sock->status = SOCK_STAT_DISCONNECT; + for (rp=res; rp!=NULL; rp=rp->ai_next) + { + /* create the socket */ + sock_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if( sock_fd < 0) + { + log_err("socket() create failed: %s\n", strerror(errno)); + rv = -4; + continue; + } + + inet_ntop(AF_INET, &(((struct sockaddr_in *)(rp->ai_addr))->sin_addr), ipstr, sizeof(ipstr)); + log_dbg("DNS resolve IP address [%s]\n", ipstr); + + /* connect to the remote server successfully */ + if(0==connect(sock_fd, rp->ai_addr, rp->ai_addrlen)) + { + memcpy(&sock->saddr, &rp->ai_addr, sizeof(sock->saddr)); + sock->fd = sock_fd; + sock->status = SOCK_STAT_CONNECTED; + rv = 0; + goto out; + } + else /* Connect to server failed. */ + { + rv = -5; + log_err("connect() to server IP[%s] failed: %s\n", ipstr, strerror(errno)); + } + + close(sock_fd); + } + + +out: + if( rv < 0 ) + { + log_err("create socket connect to [%s:%d] failed: %s\n", sock->servaddr, sock->servport, strerror(errno)); + sock->status = SOCK_STAT_DISCONNECT; + sock->fd = -1; + } + else + { + log_nrml("create socket connect to [%s:%d] successfully\n", sock->servaddr, sock->servport); + sock->status = SOCK_STAT_CONNECTED; + } + + if(res) + { + freeaddrinfo(res); + } + + return rv; +} + +int cp_sock_nonblock_connect(cp_socket_t *sock, char *servaddr, unsigned short servport) +{ + int rv = -1; + int sock_fd = -1; + char service[20]; + struct addrinfo hints, *rp; + struct addrinfo *res = NULL; + struct in_addr inaddr; + char ipstr[20]; + + if( !sock || !servaddr || !servport ) + { + log_err("Invalid input argument\n"); + return -1; + } + + if(SOCK_STAT_CONNECTED == sock->status) + { + return 0; + } + else if(SOCK_STAT_CONNECTING == sock->status) + { + goto connecting; + } + + log_nrml("Start socket connect to [%s:%d]...\n", sock->servaddr, sock->servport); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; /* Only support IPv4 */ + hints.ai_socktype = SOCK_STREAM; + + /* If Hostname is a valid IP address, then don't use name resolution */ + if( inet_aton(sock->servaddr, &inaddr) ) + { + log_info("%s is a valid IP address, don't use name resolution.\n", sock->servaddr); + hints.ai_flags |= AI_NUMERICHOST; + } + + /* Obtain address(es) matching servaddr/servport */ + snprintf(service, sizeof(service), "%d", sock->servport); + if( (rv=getaddrinfo(sock->servaddr, service, &hints, &res)) ) + { + log_err("getaddrinfo() parser [%s:%s] failed: %s\n", sock->servaddr, service, gai_strerror(rv)); + rv = -2; + goto failed; + } + + + /* getaddrinfo() returns a list of address structures. Try each + * address until we successfully connect or bind + */ + rv = -3; /* Connect failure */ + for (rp=res; rp!=NULL; rp=rp->ai_next) + { + /* Create the socket */ + sock_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if( sock_fd < 0) + { + log_err("socket() create failed: %s\n", strerror(errno)); + rv = -4; + continue; + } + + inet_ntop(AF_INET, &(((struct sockaddr_in *)(rp->ai_addr))->sin_addr), ipstr, sizeof(ipstr)); + log_dbg("DNS resolve IP address [%s]\n", ipstr); + + cp_sock_set_nonblock(sock_fd); + + /* Nono block Connect to the remote server */ + if(0==connect(sock_fd, rp->ai_addr, rp->ai_addrlen)) + { + /* Conenct to server successfully */ + memcpy(&sock->saddr, &rp->ai_addr, sizeof(sock->saddr)); + sock->fd = sock_fd; + sock->status = SOCK_STAT_CONNECTED; + rv = 0; + goto connected; + } + else + { + if(EINPROGRESS == errno) + { + /* Connect to server now in progress */ + memcpy(&sock->saddr, &rp->ai_addr, sizeof(sock->saddr)); + sock->fd = sock_fd; + sock->status = SOCK_STAT_CONNECTING; + rv = 0; + goto connecting; + } + else + { + /* Connect to server failed. */ + close(sock_fd); + rv = -5; + log_err("connect() to server failed: %s\n", strerror(errno)); + goto failed; + } + } + + close(sock_fd); + } + + +failed: + log_err("create socket connect to [%s:%d] failed: %s\n", sock->servaddr, sock->servport, strerror(errno)); + sock->status = SOCK_STAT_DISCONNECT; + sock->fd = -1; + return rv; + +connecting: + if(SOCK_STAT_CONNECTING == sock->status) + { + int len; + + len = sizeof(sock->saddr); + + errno = 0; + if( 0 == connect(sock->fd, &sock->saddr, len) ) + { + /* connect() return 0 means it already connected */ + sock->status = SOCK_STAT_CONNECTED; + rv = 0; + goto connected; + } + + /* Connect failure will continue to check */ + switch (errno) + { + case EISCONN: + sock->status = SOCK_STAT_CONNECTED; + rv = 0; + goto connected; + + case EALREADY: + case EINPROGRESS: + log_dbg("socket[%d] connect to remote [%s:%d] in progress\n", sock->fd, sock->servaddr, sock->servport); + rv = 0; + goto cleanup; + + default: + log_err("socket[%d] connect to remote [%s:%d] failed: %s\n", sock->fd, sock->servaddr, sock->servport, strerror(errno)); + sock->status = SOCK_STAT_DISCONNECT; + rv = -7; + break; + } + } + +connected: + if(SOCK_STAT_CONNECTED == sock->status) + { + rv = 0; + log_nrml("socket[%d] connected to remote server [%s:%d]\n", sock->fd, sock->servaddr, sock->servport); + goto cleanup; + } + +cleanup: + if(res) + freeaddrinfo(res); /* No longer needed */ + + return rv; +} + + +int cp_sock_set_nonblock(int sockfd) +{ + int opts; + /* + * fcntl may set: + * + * EACCES, EAGAIN: Operation is prohibited by locks held by other + * processes. Or, operation is prohibited because the file has + * been memory-mapped by another process. + * EBADF: fd is not an open file descriptor, or the command was F_SETLK + * or F_SETLKW and the file descriptor open mode doesn't match + * with the type of lock requested. + * EDEADLK: It was detected that the specified F_SETLKW command would + * cause a deadlock. + * EFAULT: lock is outside your accessible address space. + * EINTR: For F_SETLKW, the command was interrupted by a signal. For + * F_GETLK and F_SETLK, the command was interrupted by a signal + * before the lock was checked or acquired. Most likely when + * locking a remote file (e.g. locking over NFS), but can + * sometimes happen locally. + * EINVAL: For F_DUPFD, arg is negative or is greater than the maximum + * allowable value. For F_SETSIG, arg is not an allowable signal + * number. + * EMFILE: For F_DUPFD, the process already has the maximum number of + * file descriptors open. + * ENOLCK: Too many segment locks open, lock table is full, or a remote + * locking protocol failed (e.g. locking over NFS). + * EPERM: Attempted to clear the O_APPEND flag on a file that has the + * append-only attribute set. + */ + opts = fcntl(sockfd, F_GETFL); + if (opts < 0) + { + log_warn("fcntl() get socket options failure: %s\n", strerror(errno)); + return -1; + } + + opts |= O_NONBLOCK; + if (fcntl(sockfd, F_SETFL, opts) < 0) + { + log_warn("fcntl() set socket options failure: %s\n", strerror(errno)); + return -1; + } + + log_dbg("Set socket[%d] none blocking\n", sockfd); + return opts; +} + + + +void cp_sock_close(cp_socket_t *sock) +{ + close(sock->fd); + sock->fd = 0; + sock->status = SOCK_STAT_INIT; +} + diff --git a/monitord/cp_socket.h b/monitord/cp_socket.h new file mode 100644 index 0000000..ada48c1 --- /dev/null +++ b/monitord/cp_socket.h @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright: (C) 2014 Guo Wenxue<guowenxue@gmail.com> + * All rights reserved. + * + * Filename: cp_socket.h + * Description: This head file + * + * Version: 1.0.0(11/19/2014) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "11/19/2014 12:16:45 AM" + * + ********************************************************************************/ +#ifndef __CP_SOCKET_H_ +#define __CP_SOCKET_H_ + +#include <sys/types.h> +#include <sys/socket.h> + +#define DOMAIN_MAX_LEN 128 + +#define SOCK_STAT_INIT 0 +#define SOCK_STAT_CONNECTING 1 +#define SOCK_STAT_CONNECTED 2 +#define SOCK_STAT_DISCONNECT 3 + +typedef struct cp_socket_s +{ + char servaddr[DOMAIN_MAX_LEN]; /* connect server hostname or IP address */ + unsigned short servport; /* connect server port */ + int fd; /* socket fd */ + struct sockaddr saddr; /* sockaddr for none-block connect */ + unsigned char status; /* socket connect status: 0:disconnect 1:connecting 2:connected */ +} cp_socket_t; + + +/* Description: create socket object and connect to server in block mode + * $sock: socket object, if it's NULL will be malloced. + * $servaddr: connect server hostname or IP address + * $servport: connect to server port + * + * Return: socket object address + */ +extern int cp_sock_block_connect(cp_socket_t *sock, char * servaddr, unsigned short servport); + + +/* Description: set socket to none block mode */ +extern int cp_sock_set_nonblock(int sockfd); + +/* Description: create socket object and connect to server in none block mode + * $sock: socket object, if it's NULL will be malloced. + * $servaddr: connect server hostname or IP address + * $servport: connect to server port + * + * Return: 0: connecting or connected <0: connect failure + */ + +extern int cp_sock_nonblock_connect(cp_socket_t *sock, char * servaddr, unsigned short servport); + + +/* Description: close socket */ +extern void cp_sock_close(cp_socket_t *sock); + + +#endif + diff --git a/monitord/makefile b/monitord/makefile new file mode 100644 index 0000000..684dac6 --- /dev/null +++ b/monitord/makefile @@ -0,0 +1,68 @@ +#********************************************************************************* +# 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" +# +#********************************************************************************/ + +PWD=$(shell pwd) +INSTPATH=/usr/bin + +CFLAGS+=-I${PWD} +#CFLAGS+=-Wall -Werror + +LDFLAGS+=-lwiringPi +LDFLAGS+=-lpthread + +VPATH= . +SRCS = $(wildcard ${VPATH}/*.c) +OBJS = $(patsubst %.c,%.o,$(SRCS)) + +#CC=arm-linux- +export CC=${CROSS_COMPILE}gcc +export CXX=${CROSS_COMPILE}g++ +export AR=${CROSS_COMPILE}ar +export AS=${CROSS_COMPILE}as +export RANLIB=${CROSS_COMPILE}ranlib +export STRIP=${CROSS_COMPILE}strip +export CFLAGS +export LDFLAGS + +SRCFILES = $(wildcard *.c) +IMAGE_NAME=$(shell basename ${PWD}) + +all: entry binary +entry: + @echo " "; + @echo " ========================================================="; + @echo " ** Compile \"${BINARIES}\" for ${ARCH} "; + @echo " ========================================================="; + +binary: ${SRCFILES} + $(CC) $(CFLAGS) -o ${IMAGE_NAME} $^ ${LDFLAGS} + @echo " Compile over" + +tag: + @ctags --c-kinds=+defglmnstuvx --langmap=c:.c.h.ho.hem.het.hec.hev.him.hit.hic.hiv -R . + @cscope -Rbq + +install: + @cp $(IMAGE_NAME) ${INSTPATH} + +clean: + @rm -f version.h + @rm -f *.o $(IMAGE_NAME) + @rm -rf *.gdb *.a *.so *.elf* + +distclean: clean + @rm -f tags cscope* + +.PHONY: clean entry + diff --git a/monitord/monitord.c b/monitord/monitord.c new file mode 100644 index 0000000..f786fd0 --- /dev/null +++ b/monitord/monitord.c @@ -0,0 +1,186 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT System Studio + * All rights reserved. + * + * Filename: monitord.c + * Description: This file is the monitord program, which use infrared to detect human + * incoming, then turn on the LED by relay and take a photo for it. + * + * Version: 1.0.0(2018年05月29日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2018年05月29日 21时03分54秒" + * + ********************************************************************************/ + +#include <unistd.h> +#include <libgen.h> +#include <getopt.h> +#include "monitord.h" +#include "cp_logger.h" +#include "cp_proc.h" + + +/* Show program version information, can used by 'monitord --version or monitord -v'*/ +static inline void prog_version(const char *progname) +{ + printf("%s Version 1.0.0 Build(%s)\n", progname, __DATE__); + return ; +} + + +/* Show program help information, can used by 'monitord --help or monitord -h'*/ +static void prog_usage(const char *progname) +{ + prog_version(progname); + + printf("Usage: %s [OPTION]...\n", progname); + printf("Receive date from a serial port and transfer the data to remote server by socket.\n"); + printf("\nMandatory arguments to long options are mandatory for short options too:\n"); + + printf(" -d[debug ] Running in debug mode\n"); + printf(" -l[level ] Set the log level as [0..%d]\n", LOG_LEVEL_MAX-1); + printf(" -s[server ] Socket connect server host and port, format as: Hostname:Port, default as 127.0.0.1:8900\n"); + + printf(" -v[version ] Display program version\n"); + printf(" -h[help ] Display this help information\n"); + return ; +} + +void *infrared_worker(void *arg); + +int main (int argc, char **argv) +{ + const char *progname=NULL; + + int opt; + int debug = 0; /* program running information log to stdard output */ + char pid_file[64] = { 0 }; /* The file used to record the PID */ + int log_level = LOG_LEVEL_TRACE; /* program running information log to file level */ + cp_logger *logger; + char *log_file="monitord.log"; /* program running information log file name */ + + char *server="127.0.0.1:8900"; /* default connect server address, can use '-s' option to change */ + + int rv; + pthread_t tid; + + /* progranm command options */ + struct option long_options[] = { + {"debug", no_argument, NULL, 'd'}, /* monitord -d or monitord --debug, log to standard output or not */ + {"level", required_argument, NULL, 'l'}, /* monitord -l 7, specify log to file level */ + {"server", required_argument, NULL, 's'}, /* monitord -s 192.168.0.5:9999, specify server address and port */ + {"version", no_argument, NULL, 'v'}, /* monitord -v or monitord --version to check program version information */ + {"help", no_argument, NULL, 'h'}, /* monitord -h or monitord --help to get program help information */ + {NULL, 0, NULL, 0} /* array end flag */ + }; + + + progname = basename(argv[0]); + + /* Parser the command line options */ + while ((opt = getopt_long(argc, argv, "s:dl:vh", long_options, NULL)) != -1) + { + switch (opt) + { + case 'd': /* monitord -d or monitord --debug */ + debug = 1; + log_file = DBG_LOG_FILE; + break; + + case 'l': /* monitord -l 7 */ + rv = atoi(optarg); + log_level = rv>LOG_LEVEL_MAX ? LOG_LEVEL_MAX-1 : rv; + break; + + case 'v': /* monitord -v */ + prog_version(progname); + return EXIT_SUCCESS; + + case 'h': /* monitord -h */ + prog_usage(progname); + return 0; + + default: + break; + } /* end of "switch(opt)" */ + } + + + /* parser hostname and port by server*/ + { + char *ptr; + + ptr=strchr(server, ':'); + if( !ptr ) + { + printf("Invalid server host[%s], which format should be [HostName:Port] such as \"127.0.0.1:8900\"", server); + return -1; + } + } + + + + /* check program already running on background or not */ + if( !debug ) + { + snprintf(pid_file, sizeof(pid_file), "/var/run/%s.pid", progname); + if( check_daemon_running(pid_file) ) + { + printf("Programe already running, exit now.\n"); + return -1; + } + } + + /* initialise logger system */ + if( !(logger=cp_log_init(NULL, log_file, log_level, 0)) || cp_log_open()<0 ) + { + printf("Init logger system failed, program exit now...\n"); + return -1; + } + /* set program running in background */ + if( !debug ) + { + if( set_daemon_running(pid_file) ) + { + log_fatal("Set program \"%s\" running as daemon failure.\n", progname); + return -2; + } + } + + /* install signal process handler */ + cp_install_proc_signal(); + + /* start infrared detected thread */ + thread_start(&tid, infrared_worker, NULL); + + while( !g_cp_signal.stop ) + { + /* control/main thread do nothing here */ + sleep(1); + } + + return 0; +} + +void *infrared_worker(void *arg) +{ + int rv = 0; + +#if 0 + if( !arg ) + { + log_err("Invalid arguments\n"); + return NULL; + } +#endif + + log_nrml("Thread worker for infrared start\n"); + + while( !g_cp_signal.stop ) + { + } + + return NULL; +} + + diff --git a/monitord/monitord.h b/monitord/monitord.h new file mode 100644 index 0000000..24b8f70 --- /dev/null +++ b/monitord/monitord.h @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright: (C) 2018 LingYun IoT System Studio + * All rights reserved. + * + * Filename: monitord.h + * Description: This head file + * + * Version: 1.0.0(2018年05月29日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2018年05月29日 21时05分43秒" + * + ********************************************************************************/ + +#ifndef _MONITORD_H_ +#define _MONITORD_H_ + + +#endif /* ----- #ifndef _MONITORD_H_ ----- */ diff --git a/mqttd/ds18b20.c b/mqttd/ds18b20.c new file mode 100644 index 0000000..2aa9116 --- /dev/null +++ b/mqttd/ds18b20.c @@ -0,0 +1,105 @@ +/********************************************************************************* + * 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> + +/* 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) + { + printf("opendir error: %s\n", 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 ) + { + printf("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 ) + { + printf("open %s error: %s\n", w1_path, strerror(errno)); + return -4; + } + + if(read(fd, buf, sizeof(buf)) < 0) + { + printf("read %s error: %s\n", w1_path, strerror(errno)); + return -5; + } + + ptr = strstr(buf, "t="); + if( !ptr ) + { + printf("ERROR: Can not get temperature\n"); + return -6; + } + + ptr+=2; + + /* convert string value to float value */ + *temp = atof(ptr)/1000; + + close(fd); + + return 0; +} diff --git a/mqttd/ds18b20.h b/mqttd/ds18b20.h new file mode 100644 index 0000000..6c019f6 --- /dev/null +++ b/mqttd/ds18b20.h @@ -0,0 +1,19 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT System Studio + * All rights reserved. + * + * Filename: ds18b20.c + * Description: This file is temperature sensor DS18B20 code + * + * Version: 1.0.0(2018/10/14) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2018/10/14 12:13:26" + * + ********************************************************************************/ + +#ifndef __DS18B20_H +#define __DS18B20_H + +extern int ds18b20_get_temperature(float *temp); + +#endif diff --git a/mqttd/led.c b/mqttd/led.c new file mode 100644 index 0000000..23a4181 --- /dev/null +++ b/mqttd/led.c @@ -0,0 +1,60 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT System Studio + * All rights reserved. + * + * Filename: led.c + * Description: This file is used to control RGB 3-colours LED + * + * pi@raspberrypi:~ $ gpio readall show BCM and wPi pinmap + * + * LED BCM wPi + * G ---- GPIO.13 ---- GPIO.23 + * R ---- GPIO.19 ---- GPIO.24 + * B ---- GPIO.26 ---- GPIO.25 + * I ---- GND ---- + * + * 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 <wiringPi.h> +#include "led.h" + +void init_led(void) +{ + int i; + + wiringPiSetup(); + + for(i=0; i<LED_MAX; i++) + { + pinMode( led_gpio[i], OUTPUT ); + } +} + +int turn_led(int which, int cmd) +{ + if( which<0 || which>=LED_MAX ) + return -1; + + + if( OFF == cmd ) + digitalWrite (led_gpio[which], LOW); + else + digitalWrite (led_gpio[which], HIGH); + + return 0; +} + + diff --git a/mqttd/led.h b/mqttd/led.h new file mode 100644 index 0000000..dfc411c --- /dev/null +++ b/mqttd/led.h @@ -0,0 +1,48 @@ +/********************************************************************************* + * Copyright: (C) 2018 LingYun IoT System Studio + * All rights reserved. + * + * Filename: led.h + * Description: This file is used to control RGB 3-colours LED + * + * pi@raspberrypi:~ $ gpio readall show BCM and wPi pinmap + * + * LED BCM wPi + * G ---- GPIO.13 ---- GPIO.23 + * R ---- GPIO.19 ---- GPIO.24 + * B ---- GPIO.26 ---- GPIO.25 + * I ---- GND ---- + * + * 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 __LED_H +#define __LED_H + +#define OFF 0 +#define ON 1 + +/* Three LEDs code */ +enum +{ + LED_R = 0, + LED_G, + LED_B, + LED_MAX, +}; + +/* 3 LEDs WiringPi GPIO port */ + + /* LED_R LED_G LED_B */ +static int led_gpio[LED_MAX]= { 24, 23, 25 }; + + +void init_led(void); +int turn_led(int which, int cmd); + + +#endif + diff --git a/mqttd/main.c b/mqttd/main.c new file mode 100644 index 0000000..1ec682b --- /dev/null +++ b/mqttd/main.c @@ -0,0 +1,92 @@ +/********************************************************************************* + * 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 "led.h" +#include "ds18b20.h" +#include "sht20.h" + +int hal_init(void); + +/******************************************************************************** + * Description: + * Input Args: + * Output Args: + * Return Value: + ********************************************************************************/ +int main (int argc, char **argv) +{ + float temp; + float rh; + + if( hal_init() < 0 ) + { + printf("Initialise hardware failure\n"); + return -1; + } + + while(1) + { + turn_led(LED_R, ON); + sleep(1); + turn_led(LED_R, OFF); + sleep(1); + + turn_led(LED_G, ON); + sleep(1); + turn_led(LED_G, OFF); + sleep(1); + + turn_led(LED_B, ON); + sleep(1); + turn_led(LED_B, OFF); + sleep(1); + + if(sht2x_get_temp_humidity(&temp, &rh) < 0) + { + printf("SHT2X get temperature and relative humidity failure\n"); + } + else + { + printf("SHT2X Temperature=%lf ℃ relative humidity=%lf%\n", temp, rh); + } + + + if( ds18b20_get_temperature(&temp) < 0) + { + printf("DS18B20 get temperature failure\n"); + } + else + { + printf("DS18B20 get temperature=%lf ℃ \n", temp); + } + } + + return 0; +} /* ----- End of main() ----- */ + + +int hal_init(void) +{ + init_led(); + + if( sht2x_init() < 0 ) + { + printf("Initialise SHT20 failure\n"); + return -2; + } + + return 0; +} diff --git a/mqttd/makefile b/mqttd/makefile new file mode 100644 index 0000000..684dac6 --- /dev/null +++ b/mqttd/makefile @@ -0,0 +1,68 @@ +#********************************************************************************* +# 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" +# +#********************************************************************************/ + +PWD=$(shell pwd) +INSTPATH=/usr/bin + +CFLAGS+=-I${PWD} +#CFLAGS+=-Wall -Werror + +LDFLAGS+=-lwiringPi +LDFLAGS+=-lpthread + +VPATH= . +SRCS = $(wildcard ${VPATH}/*.c) +OBJS = $(patsubst %.c,%.o,$(SRCS)) + +#CC=arm-linux- +export CC=${CROSS_COMPILE}gcc +export CXX=${CROSS_COMPILE}g++ +export AR=${CROSS_COMPILE}ar +export AS=${CROSS_COMPILE}as +export RANLIB=${CROSS_COMPILE}ranlib +export STRIP=${CROSS_COMPILE}strip +export CFLAGS +export LDFLAGS + +SRCFILES = $(wildcard *.c) +IMAGE_NAME=$(shell basename ${PWD}) + +all: entry binary +entry: + @echo " "; + @echo " ========================================================="; + @echo " ** Compile \"${BINARIES}\" for ${ARCH} "; + @echo " ========================================================="; + +binary: ${SRCFILES} + $(CC) $(CFLAGS) -o ${IMAGE_NAME} $^ ${LDFLAGS} + @echo " Compile over" + +tag: + @ctags --c-kinds=+defglmnstuvx --langmap=c:.c.h.ho.hem.het.hec.hev.him.hit.hic.hiv -R . + @cscope -Rbq + +install: + @cp $(IMAGE_NAME) ${INSTPATH} + +clean: + @rm -f version.h + @rm -f *.o $(IMAGE_NAME) + @rm -rf *.gdb *.a *.so *.elf* + +distclean: clean + @rm -f tags cscope* + +.PHONY: clean entry + diff --git a/mqttd/sht20.c b/mqttd/sht20.c new file mode 100644 index 0000000..66af731 --- /dev/null +++ b/mqttd/sht20.c @@ -0,0 +1,439 @@ +/********************************************************************************* + * 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" + +static int s_sht2x_fd = -1; + +void sht2x_term(void) +{ + close(s_sht2x_fd); + s_sht2x_fd = -1; + printf("Terminate SHT2X\n"); +} + +#ifdef I2C_API_RDWR /* Use I2C userspace driver read/write API */ + +int sht2x_softreset(int fd) +{ + uint8_t buf[4]; + + if( fd<0 ) + { + printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); + 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) + { + printf("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 ) + { + printf("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 ) + { + printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); + return -1; + } + + if( s_sht2x_fd < 0 ) + { + if( sht2x_init() < 0) + { + printf("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 ) + { + printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); + return -1; + } + + if( s_sht2x_fd < 0 ) + { + if( sht2x_init() < 0) + { + printf("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 ) + { + printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); + 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 ) + { + printf("%s() ioctl failure: %s\n", __func__, 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) + { + printf("i2c device open failed: %s\n", strerror(errno)); + return -1; + } + + if( sht2x_softreset(s_sht2x_fd) < 0 ) + { + printf("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 ) + { + printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); + return -1; + } + + if( s_sht2x_fd < 0 ) + { + if( sht2x_init() < 0) + { + printf("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 ) + { + printf("%s() ioctl failure: %s\n", __func__, 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 ) + { + printf("%s() ioctl failure: %s\n", __func__, 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 ) + { + printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); + return -1; + } + + if( s_sht2x_fd < 0 ) + { + if( sht2x_init() < 0) + { + printf("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 ) + { + printf("%s() ioctl failure: %s\n", __func__, 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 ) + { + printf("%s() ioctl failure: %s\n", __func__, 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 ) + { + printf("%s() ioctl failure: %s\n", __func__, 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 ) + { + printf("%s() ioctl failure: %s\n", __func__, 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 + diff --git a/mqttd/sht20.h b/mqttd/sht20.h new file mode 100644 index 0000000..467fbfa --- /dev/null +++ b/mqttd/sht20.h @@ -0,0 +1,74 @@ +/********************************************************************************* + * 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 <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 */ + +void sht2x_term(void); +int sht2x_init(void); +int sht2x_get_serialnumber(uint8_t *serialnumber, int size); +int sht2x_get_temp_humidity(float *temp, float *rh); + +static inline void msleep(unsigned long ms) +{ + struct timespec cSleep; + unsigned long ulTmp; + + cSleep.tv_sec = ms / 1000; + if (cSleep.tv_sec == 0) + { + ulTmp = ms * 10000; + cSleep.tv_nsec = ulTmp * 100; + } + else + { + cSleep.tv_nsec = 0; + } + + nanosleep(&cSleep, 0); +} + +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 ; +} + +#endif + -- Gitblit v1.9.1