/********************************************************************************* * 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 * ChangeLog: 1, Release initial version on "2020年04月14日 00时52分56秒" * ********************************************************************************/ #include #include #include #include #include #include "logger.h" #include "proc.h" #include "socket.h" #include "tlv_pack.h" #include "crc-itu-t.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 int parser_tlvpack(cli_buf_t *cli_buf); 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; ifd = 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 { cli_buf->bytes += rv; log_dbg("socket[%d] receive %d bytes data\n", fd, rv); } parser_tlvpack(cli_buf); } } sleep(1); } logger_term(); return 0; } static int parser_tlvpack(cli_buf_t *cli_buf) { int i; int left_bytes; unsigned short crc16; tlv_pack_t *tlv_pack; if( !cli_buf ) { log_err("Invalid input arguments\n"); return -1; } log_info("start to parser client buffer %d bytes data:\n", cli_buf->bytes); logger_dump(LOG_LEVEL_INFO, cli_buf->buf, cli_buf->bytes); if( cli_buf->bytes < TLV_MIN_SIZE ) { log_warn("TLV packet bytes less than min. size\n"); return -2; } PARSER_AGAIN: for(i=0; ibytes; i++) { if( PACK_HEADER == (unsigned char )cli_buf->buf[i] ) { log_dbg("found TLV packet header on [%d]\n", i); left_bytes = cli_buf->bytes - i; if(left_bytes < TLV_MIN_SIZE) { log_warn("TLV packet bytes less than min. size\n"); memmove(cli_buf->buf, &cli_buf->buf[i], left_bytes); cli_buf->bytes = left_bytes; break; } tlv_pack = (tlv_pack_t *) &cli_buf->buf[i]; log_info("header: 0x%02x tag: 0x%02x len: 0x%02x\n", tlv_pack->header, tlv_pack->tag, tlv_pack->len); if(tlv_pack->len > left_bytes ) { log_err("TLV packet not integrated, continue to receive left data\n"); memmove(cli_buf->buf, &cli_buf->buf[i], left_bytes); cli_buf->bytes = left_bytes; break; } if( tlv_pack->len > TLV_MAX_SIZE ) { log_err("TLV packet length more than max. length, maybe found wrong header?\n"); /* skip current header */ left_bytes -= 1; memmove(cli_buf->buf, &cli_buf->buf[i+1], left_bytes); cli_buf->bytes = left_bytes; goto PARSER_AGAIN; } crc16 = crc_itu_t(MAGIC_CRC, (unsigned char *)&cli_buf->buf[i], tlv_pack->len-2); if( crc16 != bytes_to_ushort((unsigned char *)&cli_buf->buf[i+tlv_pack->len-2], 2) ) { log_err("TLV packet CRC check error, maybe found wrong header?\n"); /* skip current header */ left_bytes -= 1; memmove(cli_buf->buf, &cli_buf->buf[i+1], left_bytes); cli_buf->bytes = left_bytes; goto PARSER_AGAIN; } if( tlv_pack->tag == TAG_SN ) { log_nrml("Found SN TLV data:\n"); dump_buf(tlv_pack->data, tlv_pack->len-TLV_FIXED_SIZE); } else if( tlv_pack->tag == TAG_TEMP ) { log_nrml("Found temperature TLV data:\n"); dump_buf(tlv_pack->data, tlv_pack->len-TLV_FIXED_SIZE); } else if( tlv_pack->tag == TAG_TIME ) { log_nrml("Found date time TLV data:\n"); dump_buf(tlv_pack->data, tlv_pack->len-TLV_FIXED_SIZE); } left_bytes -= tlv_pack->len; memmove(cli_buf->buf, &cli_buf->buf[i+tlv_pack->len], left_bytes); cli_buf->bytes = left_bytes; log_info("left %d bytes data:\n", cli_buf->bytes); logger_dump(LOG_LEVEL_INFO, cli_buf->buf, cli_buf->bytes); goto PARSER_AGAIN; } } 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 ); }