| | |
| | | |
| | | #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) |
| | | { |
| | |
| | | { |
| | | int opt; |
| | | int i = 0; |
| | | //int rv = 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; |
| | |
| | | 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'}, |
| | |
| | | }; |
| | | |
| | | 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, "c:dl:vh", long_options, NULL)) != -1) |
| | | 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; |
| | |
| | | } |
| | | } |
| | | |
| | | 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 ) |
| | |
| | | printf("Programe already running, exit now.\n"); |
| | | return -1; |
| | | } |
| | | |
| | | set_daemon_running(pid_file); |
| | | } |
| | | |
| | | /* initial and open logger system */ |
| | |
| | | /* install signal proc handler */ |
| | | install_proc_signal(); |
| | | |
| | | log_nrml("Program start running\n"); |
| | | 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 ) |
| | | { |
| | | log_dbg("Program still running\n"); |
| | | sleep(3); |
| | | /* 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 |
| | | { |
| | | 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; i<cli_buf->bytes; 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 ); |
| | | } |
| | | |