/*********************************************************************************
|
* Copyright: (C) 2020 LingYun IoT System Studio
|
* All rights reserved.
|
*
|
* Filename: tlv_server.c
|
* Description: This is a socket server program to receive RPi's temperature
|
* from socket client and save it in sqlite database.
|
*
|
* Version: 1.0.0(2020年04月14日)
|
* Author: Guo Wenxue <guowenxue@gmail.com>
|
* ChangeLog: 1, Release initial version on "2020年04月14日 00时52分56秒"
|
*
|
********************************************************************************/
|
#include <stdio.h>
|
#include <unistd.h>
|
#include <string.h>
|
#include <getopt.h>
|
#include <libgen.h>
|
|
#include "logger.h"
|
#include "proc.h"
|
#include "socket.h"
|
|
#define PROG_VERSION "1.0.0"
|
|
#define DEF_LOG_FILE "tlv_server.log"
|
#define DEF_LISTEN_IP "0.0.0.0"
|
#define DEF_LISTEN_PORT 10086
|
|
#define MAX_EVENTS 512
|
|
|
/* Every client need get a stand alone buffer to save TLV packet data */
|
typedef struct cli_buf_s
|
{
|
int fd; /* client fd */
|
char buf[512]; /* client receive buffer */
|
int bytes; /* left bytes in buffer */
|
struct list_head list; /* all client buffer saved in a list */
|
} cli_buf_t;
|
|
|
static void term_socket_client(int epollfd, int fd, struct list_head *buf_list);
|
|
static void banner(void)
|
{
|
printf("Version %s build on %s %s\n", PROG_VERSION, __DATE__, __TIME__);
|
printf("Copyright (C) 2020 LingYun IoT System Studio.\n\n");
|
|
return ;
|
}
|
|
static void program_usage(const char *progname)
|
{
|
printf("Usage: %s [OPTION]...\n", progname);
|
printf("This program receive RPi's temperature from socket client and save it in sqlite database.\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(" -h[help ] Display this help information\n");
|
printf(" -v[version ] Display the program version\n");
|
printf("\n");
|
|
banner();
|
return ;
|
}
|
|
int main (int argc, char *argv[])
|
{
|
int opt;
|
int i = 0;
|
int rv = 0;
|
int debug = 0;
|
int port = DEF_LISTEN_PORT;
|
|
char pid_file[64] = { 0 }; /* The file used to record the PID */
|
const char *progname=NULL;
|
|
int log_level = LOG_LEVEL_NRML;
|
char *log_file = DEF_LOG_FILE;
|
logger_t logger;
|
|
socket_t sock;
|
int epollfd;
|
int connfd;
|
int fd;
|
int nfds;
|
struct epoll_event event_array[MAX_EVENTS];
|
|
cli_buf_t *cli_buf;
|
cli_buf_t *node;
|
struct list_head buf_list; /* every client get a stand alone buffer saved in the list */
|
|
struct option long_options[] = {
|
{"port", required_argument, NULL, 'p'},
|
{"debug", no_argument, NULL, 'd'},
|
{"level", required_argument, NULL, 'l'},
|
{"version", no_argument, NULL, 'v'},
|
{"help", no_argument, NULL, 'h'},
|
{NULL, 0, NULL, 0}
|
};
|
|
memset(&logger, 0, sizeof(logger));
|
memset(&sock, 0, sizeof(sock));
|
INIT_LIST_HEAD(&buf_list);
|
|
progname = basename(argv[0]); /* get program name */
|
|
/* parser the command line parameters */
|
while ((opt = getopt_long(argc, argv, "p:dl:vh", long_options, NULL)) != -1)
|
{
|
switch (opt)
|
{
|
case 'p': /* listen port */
|
port = atoi(optarg);
|
break;
|
|
case 'd': /* set debug running */
|
debug = 1;
|
log_level= LOG_LEVEL_DEBUG;
|
log_file = NULL; /* use standard output */
|
break;
|
|
case 'l': /* set the log level */
|
i = atoi(optarg);
|
log_level = i>LOG_LEVEL_MAX ? LOG_LEVEL_MAX-1 : i;
|
logger.flag |= FLAG_LOGGER_LEVEL_OPT;
|
break;
|
|
case 'v': /* print software version */
|
banner();
|
return EXIT_SUCCESS;
|
|
case 'h': /* print help information */
|
program_usage(progname);
|
return 0;
|
|
default:
|
break;
|
}
|
}
|
|
if( port <= 0 )
|
{
|
printf("ERROR: no listen port specified, refer to program usage\n\n");
|
program_usage(progname);
|
}
|
|
/* check program already running or not, if already running then exit, or set running as daemon */
|
snprintf(pid_file, sizeof(pid_file), "/var/run/%s.pid", progname);
|
if( !debug )
|
{
|
if( check_daemon_running(pid_file) )
|
{
|
printf("Programe already running, exit now.\n");
|
return -1;
|
}
|
|
set_daemon_running(pid_file);
|
}
|
|
/* initial and open logger system */
|
if( logger_init(&logger, log_file, log_level, 512)<0 || logger_open()<0 )
|
{
|
printf("ERROR: Initialise logger system failure\n");
|
return -1;
|
}
|
|
/* install signal proc handler */
|
install_proc_signal();
|
|
log_nrml("TLV server program start running\n");
|
|
/* set open file description number to max */
|
set_socket_rlimit();
|
|
/* initial listen socket */
|
if( socket_listen(&sock, DEF_LISTEN_IP, port) < 0 )
|
{
|
log_err("create listen socket failure\n");
|
return -2;
|
}
|
|
/* create epoll and put listen socket into it */
|
if( (epollfd=epoll_init(MAX_EVENTS, sock.fd)) < 0 )
|
{
|
log_err("initial epoll for listen socket failure\n");
|
return -2;
|
}
|
|
/* g_signal.stop defined in proc.c, and will be set when catch stop signal */
|
while( !g_signal.stop )
|
{
|
/* epoll wailt and program will blocked here */
|
nfds = epoll_wait(epollfd, event_array, MAX_EVENTS, -1 /* never timeout */);
|
if( nfds <= 0 )
|
{
|
log_err("epoll_wait failure or timeout: %s\n", strerror(errno));
|
continue;
|
}
|
|
/* nfds>0 is the active events count */
|
for(i=0; i<nfds; i++)
|
{
|
fd = event_array[i].data.fd;
|
|
/* epoll get error */
|
if ( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) )
|
{
|
log_err("epoll_wait get error on fd[%d]: %s\n", fd, strerror(errno));
|
if( fd != sock.fd )
|
term_socket_client(epollfd, event_array[i].data.fd, &buf_list);
|
|
continue;
|
}
|
|
/* listen socket get event means new client start connect now */
|
if( fd == sock.fd )
|
{
|
/* accept new client socket */
|
if( (connfd=accept(sock.fd, (struct sockaddr *)NULL, NULL)) < 0)
|
{
|
log_nrml("accept new client failure: %s\n", strerror(errno));
|
continue;
|
}
|
|
/* add new client socket into epoll */
|
if( epoll_add(epollfd, connfd) < 0 )
|
{
|
close(connfd);
|
log_err("epoll add client socket failure, rv=%d\n", rv);
|
}
|
|
/* malloc client buffer and add it into list */
|
if( NULL != (cli_buf=(cli_buf_t *)malloc(sizeof(*cli_buf))) )
|
{
|
memset(cli_buf, 0, sizeof(cli_buf));
|
cli_buf->fd = connfd;
|
list_add_tail(&cli_buf->list, &buf_list);
|
log_info("alloc and add socket[%d] buffer[%p] into list\n", cli_buf->fd, cli_buf);
|
}
|
|
log_nrml("accept and add new client socket[%d] ok.\n", connfd);
|
}
|
/* already connected client socket get data arrive */
|
else
|
{
|
cli_buf = NULL;
|
|
/* Use list_for_each_entry to find the client buf in buf list */
|
list_for_each_entry(node, &buf_list, list)
|
{
|
if( node->fd == fd )
|
{
|
log_info("found socket[%d] buffer[%p] in list\n", fd, node);
|
cli_buf = node;
|
}
|
}
|
|
if( !cli_buf )
|
{
|
log_err("can not find socket[%d] buffer and close it now\n", fd);
|
term_socket_client(epollfd, fd, NULL);
|
}
|
|
rv=read(fd, &cli_buf->buf[cli_buf->bytes], sizeof(cli_buf->buf)-cli_buf->bytes);
|
if( rv <= 0 )
|
{
|
log_warn("socket[%d] read failure [%s] or disconncet, terminate it now.\n",
|
fd, strerror(errno));
|
|
term_socket_client(epollfd, fd, &buf_list);
|
continue;
|
}
|
else
|
{
|
log_dbg("socket[%d] receive %d bytes data\n", fd, rv);
|
logger_dump(LOG_LEVEL_INFO, cli_buf->buf, rv);
|
}
|
}
|
}
|
|
sleep(1);
|
}
|
|
logger_term();
|
|
return 0;
|
}
|
|
static void term_socket_client(int epollfd, int fd, struct list_head *buf_list)
|
{
|
cli_buf_t *buf, *tmp;
|
|
log_warn("start terminatl client socket[%d]\n", fd);
|
|
/* if get buf_list, then remove buf from list */
|
if( buf_list )
|
{
|
/* Use list_for_each_entry_safe to travel the socket list and destroy the buffer */
|
list_for_each_entry_safe(buf, tmp, buf_list, list)
|
{
|
if( buf->fd == fd )
|
{
|
list_del(&buf->list);
|
free(buf);
|
log_info("free socket[%d] buffer[%p]\n", fd, buf);
|
break;
|
}
|
}
|
}
|
|
epoll_del(epollfd, fd);
|
close( fd );
|
}
|