From dc4b04335bd8086487dfcd332082b324e3bbdf3c Mon Sep 17 00:00:00 2001 From: Guo Wenxue <guowenxue@gmail.com> Date: Mon, 18 Apr 2022 21:11:01 +0800 Subject: [PATCH] Add socket project --- apue/project_socket/src/sqlite_blob.c | 265 +++++ apue/project_socket/do.sh | 4 apue/project_socket/main/server_main.c | 278 ++++++ apue/project_socket/makefile | 16 apue/project_socket/src/ds18b20.c | 113 ++ apue/project_socket/src/util_time.h | 186 ++++ apue/project_socket/main/makefile | 20 apue/project_socket/src/socket.h | 100 ++ apue/project_socket/src/sqlite_blob.h | 60 + apue/project_socket/src/socket.c | 465 ++++++++++ apue/project_socket/libs/cjson/build.sh | 42 apue/project_socket/src/packet.h | 59 + apue/project_socket/src/makefile | 29 apue/project_socket/main/client_main.c | 251 +++++ apue/project_socket/src/logger.h | 52 + apue/project_socket/src/packet.c | 67 + apue/project_socket/libs/makefile | 10 apue/project_socket/src/util_proc.c | 433 +++++++++ apue/project_socket/src/util_proc.h | 65 + apue/project_socket/src/ds18b20.h | 31 apue/project_socket/libs/sqlite/build.sh | 44 apue/project_socket/src/logger.c | 165 +++ 22 files changed, 2,755 insertions(+), 0 deletions(-) diff --git a/apue/project_socket/do.sh b/apue/project_socket/do.sh new file mode 100755 index 0000000..b07abc2 --- /dev/null +++ b/apue/project_socket/do.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +make +./main/client_main -i 127.0.0.1 -p 8888 -I 30 -d diff --git a/apue/project_socket/libs/cjson/build.sh b/apue/project_socket/libs/cjson/build.sh new file mode 100755 index 0000000..2e72a9d --- /dev/null +++ b/apue/project_socket/libs/cjson/build.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# libraries install path +INST_PATH=`pwd`/../install + +# LingYun studio FTP server address for all the open source code +LYFTP_SRC=ftp://master.iot-yun.club/src/ + +# set shell script exit when any command failure +set -e + +# funciton used to build cjson source code +function build_cjson() +{ + SRC_NAME=cJSON-1.7.15 + + if [ -L $INST_PATH/lib/libcjson.so ] ; then + echo "$SRC_NAME already compile and installed" + return ; + fi + + # If source code tarball file not exist, it will download the packet. + if [ ! -f ${SRC_NAME}.tar.gz ] ; then + wget ${LYFTP_SRC}/${SRC_NAME}.tar.gz + fi + + + # If source code folder not exist, decompress the tarball packet + if [ ! -d ${SRC_NAME} ] ; then + tar -xzf ${SRC_NAME}.tar.gz + fi + + cd ${SRC_NAME} + + make && make DESTDIR=${INST_PATH} PREFIX=/ install + + cd - +} + +# start build cjson + +build_cjson diff --git a/apue/project_socket/libs/makefile b/apue/project_socket/libs/makefile new file mode 100644 index 0000000..8b2a24d --- /dev/null +++ b/apue/project_socket/libs/makefile @@ -0,0 +1,10 @@ +all: + cd sqlite && ./build.sh + cd cjson && ./build.sh + +clean: + cd sqlite && rm -rf sqlite* + cd cjson && rm -rf c* + +distclean: clean + rm -rf install diff --git a/apue/project_socket/libs/sqlite/build.sh b/apue/project_socket/libs/sqlite/build.sh new file mode 100755 index 0000000..10e13a0 --- /dev/null +++ b/apue/project_socket/libs/sqlite/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# library install path +INST_PATH=`pwd`/../install + +# LingYun studio FTP server address for all the open source code +LYFTP_SRC=ftp://master.iot-yun.club/src/ + +# set shell script exit when any command failure +set -e + +#define a funciton to build sqlite source code +function build_sqlite() +{ + SRC_NAME=sqlite-autoconf-3380200 + + if [ -L $INST_PATH/lib/libsqlite3.so ] ; then + echo "$SRC_NAME already compile and installed" + return ; + fi + + # If source code tarball file not exist, it will download the packet. + if [ ! -f ${SRC_NAME}.tar.gz ] ; then + #wget https://sqlite.org/2022/${SRC_NAME}.tar.gz + wget ${LYFTP_SRC}/${SRC_NAME}.tar.gz + fi + + + # If source code folder not exist, decompress the tarball packet + if [ ! -d ${SRC_NAME} ] ; then + tar -xzf ${SRC_NAME}.tar.gz + fi + + cd ${SRC_NAME} + + ./configure --prefix=${INST_PATH} --enable-static + + make && make install +} + + +# call function to start build sqlite + +build_sqlite diff --git a/apue/project_socket/main/client_main.c b/apue/project_socket/main/client_main.c new file mode 100644 index 0000000..9c8c604 --- /dev/null +++ b/apue/project_socket/main/client_main.c @@ -0,0 +1,251 @@ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <getopt.h> +#include <time.h> + +#include "logger.h" +#include "ds18b20.h" +#include "packet.h" +#include "socket.h" +#include "sqlite_blob.h" +#include "util_proc.h" + + +void print_usage(char *progname) +{ + printf("%s usage: \n", progname); + printf("-i(--ipaddr) : sepcify server IP address\n"); + printf("-p(--port) : sepcify server port.\n"); + printf("-I(--interval): sepcify report interval time\n"); + printf("-s(--sn) : sepcify device serial number\n"); + printf("-d(--debug) : run as debug mode\n"); + printf("-h(--Help) : print this help information.\n"); + + return ; +} + +static int check_sample_time(time_t *last_time, int interval); + +int main(int argc, char **argv) +{ + int rv = -1; + int pr_times = 0; + int port; + char *serverip; + int interval = 30; /* default report termperature every 30 seconds */ + int sn = 1; /* default serial number for device ID */ + int debug = 0; /* running in debug mode or not */ + char *logfile="client.log"; + + socket_ctx_t sock; + time_t last_time = 0; + int sample_flag = 0; + + char pack_buf[1024]; + int pack_bytes; + pack_info_t pack_info; + pack_proc_t pack_proc = packet_string_pack; /* use string packet */ + + struct option opts[] = { + {"ipaddr", required_argument, NULL, 'i'}, + {"port", required_argument, NULL, 'p'}, + {"interval", no_argument, NULL, 'I'}, + {"debug", no_argument, NULL, 'd'}, + {"sn", required_argument, NULL, 's'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + while( (rv=getopt_long(argc, argv, "i:p:dI:s:h", opts, NULL)) != -1 ) + { + switch(rv) + { + case 'i': + serverip=optarg; + break; + + case 'p': + port=atoi(optarg); + break; + + case 'd': + debug=1; + break; + + case 'I': + interval=atoi(optarg); + break; + + case 's': + sn=atoi(optarg); + break; + + case 'h': + print_usage(argv[0]); + return 0; + } + + } + + if( !serverip || !port ) + { + print_usage(argv[0]); + return 0; + } + + if( debug ) + { + /* set logger to standard output with level debug */ + logger_init(NULL, LOG_LEVEL_DEBUG); + log_info("set program running on debug now.\n"); + } + else + { + /* set logger to $logfile with level info */ + if( logger_init(logfile, LOG_LEVEL_INFO) < 0 ) + { + fprintf(stderr, "Initial logger file '%s' failure: %s\n", strerror(errno)); + return 1; + } + + log_info("set program running on background now.\n"); + daemon(1, 1); /* don't change work path, don't close opened file descriptor */ + } + + install_default_signal(); + + log_info("program start running.\n"); + + if( database_init("client.db") < 0 ) + { + return 2; + } + + socket_init(&sock, serverip, port); + + while( !g_signal.stop ) + { + /* +----------------------------------+ + * | check and sample temperature | + * +----------------------------------+*/ + + sample_flag = 0; /* clear sample flag */ + + if( check_sample_time(&last_time, interval) ) + { + log_debug("start DS18B20 sample termperature\n"); + + if( (rv=ds18b20_get_temperature(&pack_info.temper)) < 0 ) + { + log_error("DS18B20 sample temperature failure, rv=%d\n", rv); + continue; + } + log_info("DS18B20 sample termperature %d.%d oC\n", + temper_integer(pack_info.temper), temper_fract(pack_info.temper)); + + get_devid(pack_info.devid, DEVID_LEN, sn); + get_time(pack_info.strtime, TIME_LEN); + + pack_bytes = pack_proc(&pack_info, pack_buf, sizeof(pack_buf)); + sample_flag = 1; /* set sample flag */ + } + + /* +---------------------------------+ + * | check and do socket connect | + * +---------------------------------+*/ + + /* start connect to server if not connected */ + if( sock.fd < 0 ) + { + if( 0 == (pr_times % 10) ) + { + log_info("socket not connect, start connect it now.\n"); + } + socket_connect(&sock); + } + + /* check socket connected or not */ + if( sock_check_connect(sock.fd) < 0 ) + { + pr_times ++; + if( 0 == (pr_times % 10) ) + { + log_error("socket got disconnected, terminate it and reconnect now.\n"); + pr_times = 0; + } + socket_term(&sock); /* close the soket */ + } + + /* socket disconnected */ + if( sock.fd < 0 ) + { + if( sample_flag ) + { + blobdb_push_packet(pack_buf, pack_bytes); + } + } + else /* socket connected */ + { + /* +---------------------------------+ + * | socket send sample packet | + * +---------------------------------+*/ + if( sample_flag ) + { + log_debug("socket send sample packet bytes[%d]: %s\n", pack_bytes, pack_buf); + if( socket_send(&sock, pack_buf, pack_bytes) < 0 ) + { + log_warn("socket send sample packet failure, save it in database now.\n"); + blobdb_push_packet(pack_buf, pack_bytes); + socket_term(&sock); /* close the soket */ + } + } + + /* +---------------------------------+ + * | socket send packet in database | + * +---------------------------------+*/ + if( !blobdb_pop_packet(pack_buf, sizeof(pack_buf), &pack_bytes) ) + { + log_debug("socket send database packet bytes[%d]: %s\n", pack_bytes, pack_buf); + if( socket_send(&sock, pack_buf, pack_bytes) < 0 ) + { + log_error("socket send database packet failure"); + socket_term(&sock); /* close the soket */ + } + else + { + log_warn("socket send database packet okay, remove it from database now.\n"); + blobdb_del_packet(); + } + } + } + + sleep(1); + } + + socket_term(&sock); + database_term(); + + return 0; +} + +int check_sample_time(time_t *last_time, int interval) +{ + int need = 0; /* no need sample now */ + time_t now; + + time(&now); + + if( now >= *last_time+interval ) + { + need = 1; /* need sample now */ + *last_time = now; + } + + return need; +} diff --git a/apue/project_socket/main/makefile b/apue/project_socket/main/makefile new file mode 100644 index 0000000..382b856 --- /dev/null +++ b/apue/project_socket/main/makefile @@ -0,0 +1,20 @@ + +LIBS_PATH=`pwd`/../libs/install/ + +CFLAGS += -I ${LIBS_PATH}/include/ +LDFLAGS += -L ${LIBS_PATH}/lib/ -lbooster +LDFLAGS += -lpthread -lsqlite3 + +SRCFILES = $(wildcard *.c) +BINARIES=$(SRCFILES:%.c=%) + +all: clean ${BINARIES} + +%: %.c + $(CC) $(CFLAGS) -o $@ $< ${LDFLAGS} + +clean: + rm -f ${BINARIES} + +distclean: clean + rm -f *.log *.db diff --git a/apue/project_socket/main/server_main.c b/apue/project_socket/main/server_main.c new file mode 100644 index 0000000..6e69fcd --- /dev/null +++ b/apue/project_socket/main/server_main.c @@ -0,0 +1,278 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <pthread.h> +#include <getopt.h> +#include <libgen.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/epoll.h> +#include <sys/resource.h> + +#define MAX_EVENTS 512 +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +static inline void print_usage(char *progname); +int socket_server_init(char *listen_ip, int listen_port); +void set_socket_rlimit(void); + +int main(int argc, char **argv) +{ + int listenfd, connfd; + int serv_port = 0; + int daemon_run = 0; + char *progname = NULL; + int opt; + int rv; + int i, j; + int found; + char buf[1024]; + + int epollfd; + struct epoll_event event; + struct epoll_event event_array[MAX_EVENTS]; + int events; + + struct option long_options[] = + { + {"daemon", no_argument, NULL, 'b'}, + {"port", required_argument, NULL, 'p'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + progname = basename(argv[0]); + + /* Parser the command line parameters */ + while ((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1) + { + switch (opt) + { + case 'b': + daemon_run=1; + break; + + case 'p': + serv_port = atoi(optarg); + break; + + case 'h': /* Get help information */ + print_usage(progname); + return EXIT_SUCCESS; + + default: + break; + } + } + + if( !serv_port ) + { + print_usage(progname); + return -1; + } + + set_socket_rlimit(); /* set max open socket count */ + + if( (listenfd=socket_server_init(NULL, serv_port)) < 0 ) + { + printf("ERROR: %s server listen on port %d failure\n", argv[0],serv_port); + return -2; + } + printf("%s server start to listen on port %d\n", argv[0],serv_port); + + + /* set program running on background */ + if( daemon_run ) + { + daemon(0, 0); + } + + if( (epollfd=epoll_create(MAX_EVENTS)) < 0 ) + { + printf("epoll_create() failure: %s\n", strerror(errno)); + return -3; + } + + //event.events = EPOLLIN|EPOLLET; + event.events = EPOLLIN; + event.data.fd = listenfd; + + if( epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event) < 0) + { + printf("epoll add listen socket failure: %s\n", strerror(errno)); + return -4; + } + + + for ( ; ; ) + { + /* program will blocked here */ + events = epoll_wait(epollfd, event_array, MAX_EVENTS, -1); + if(events < 0) + { + printf("epoll failure: %s\n", strerror(errno)); + break; + } + else if(events == 0) + { + printf("epoll get timeout\n"); + continue; + } + + /* rv>0 is the active events count */ + for(i=0; i<events; i++) + { + if ( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) ) + { + printf("epoll_wait get error on fd[%d]: %s\n", event_array[i].data.fd, strerror(errno)); + epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL); + close(event_array[i].data.fd); + } + + /* listen socket get event means new client start connect now */ + if( event_array[i].data.fd == listenfd ) + { + if( (connfd=accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0) + { + printf("accept new client failure: %s\n", strerror(errno)); + continue; + } + + event.data.fd = connfd; + //event.events = EPOLLIN|EPOLLET; + event.events = EPOLLIN; + if( epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event) < 0 ) + { + printf("epoll add client socket failure: %s\n", strerror(errno)); + close(event_array[i].data.fd); + continue; + } + printf("epoll add new client socket[%d] ok.\n", connfd); + } + else /* already connected client socket get data incoming */ + { + if( (rv=read(event_array[i].data.fd, buf, sizeof(buf))) <= 0) + { + printf("socket[%d] read failure or get disconncet and will be removed.\n", event_array[i].data.fd); + epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL); + close(event_array[i].data.fd); + continue; + } + else + { + printf("socket[%d] read get %d bytes data\n", event_array[i].data.fd, rv); + + /* convert letter from lowercase to uppercase */ + for(j=0; j<rv; j++) + buf[j]=toupper(buf[j]); + + if( write(event_array[i].data.fd, buf, rv) < 0 ) + { + printf("socket[%d] write failure: %s\n", event_array[i].data.fd, strerror(errno)); + epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL); + close(event_array[i].data.fd); + } + } + } + } /* for(i=0; i<rv; i++) */ + } /* while(1) */ + +CleanUp: + close(listenfd); + return 0; +} + + +static inline void print_usage(char *progname) +{ + printf("Usage: %s [OPTION]...\n", progname); + + printf(" %s is a socket server program, which used to verify client and echo back string from it\n", progname); + printf("\nMandatory arguments to long options are mandatory for short options too:\n"); + + printf(" -b[daemon ] set program running on background\n"); + printf(" -p[port ] Socket server port address\n"); + printf(" -h[help ] Display this help information\n"); + + + printf("\nExample: %s -b -p 8900\n", progname); + return ; +} + + +int socket_server_init(char *listen_ip, int listen_port) +{ + struct sockaddr_in servaddr; + int rv = 0; + int on = 1; + int listenfd; + + if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno)); + return -1; + } + + /* Set socket port reuseable, fix 'Address already in use' bug when socket server restart */ + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(listen_port); + + if( !listen_ip ) /* Listen all the local IP address */ + { + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); + } + else /* listen the specified IP address */ + { + if (inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0) + { + printf("inet_pton() set listen IP address failure.\n"); + rv = -2; + goto CleanUp; + } + } + + + if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) + { + printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno)); + rv = -3; + goto CleanUp; + } + + if(listen(listenfd, 13) < 0) + { + printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno)); + rv = -4; + goto CleanUp; + } + +CleanUp: + if(rv<0) + close(listenfd); + else + rv = listenfd; + + return rv; +} + +/* Set open file description count to max */ +void set_socket_rlimit(void) +{ + struct rlimit limit = {0}; + + getrlimit(RLIMIT_NOFILE, &limit ); + limit.rlim_cur = limit.rlim_max; + setrlimit(RLIMIT_NOFILE, &limit ); + + printf("set socket open fd max count to %d\n", limit.rlim_max); +} + diff --git a/apue/project_socket/makefile b/apue/project_socket/makefile new file mode 100644 index 0000000..52c4271 --- /dev/null +++ b/apue/project_socket/makefile @@ -0,0 +1,16 @@ + +all: + make -C libs + make -C src && make install -C src + make -C main + +clean: + make clean -C libs + make clean -C src + make clean -C main + +distclean: + make distclean -C libs + make distclean -C src + make distclean -C main + rm -f *.log *.db diff --git a/apue/project_socket/src/ds18b20.c b/apue/project_socket/src/ds18b20.c new file mode 100644 index 0000000..d0ea616 --- /dev/null +++ b/apue/project_socket/src/ds18b20.c @@ -0,0 +1,113 @@ +/********************************************************************************* + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: ds18b20.c + * Description: This file is get temperature by DS18B20 on RaspberryPi + * + * Version: 1.0.0(2020年04月15日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2020年04月15日 23时14分21秒" + * + ********************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <time.h> +#include <errno.h> + +#include "logger.h" + +/* File Content: + pi@raspberrypi:~/guowenxue $ cat /sys/bus/w1/devices/28-041731f7c0ff/w1_slave + 3a 01 4b 46 7f ff 0c 10 a5 : crc=a5 YES + 3a 01 4b 46 7f ff 0c 10 a5 t=19625 + */ + +int ds18b20_get_temperature(uint16_t *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; + uint8_t *byte; + float value; + int found = 0; + + if( !temp ) + { + log_error("ERROR: Invalid input arguments\n"); + return -1; + } + + /*+-------------------------------------------------------------------+ + *| open dierectory /sys/bus/w1/devices to get chipset Serial Number | + *+-------------------------------------------------------------------+*/ + if((dirp = opendir(w1_path)) == NULL) + { + log_error("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 ) + { + log_error("Can not find ds18b20 in %s\n", w1_path); + return -2; + } + + /* get DS18B20 sample file full path: /sys/bus/w1/devices/28-xxxx/w1_slave */ + strncat(w1_path, chip, sizeof(w1_path)-strlen(w1_path)); + strncat(w1_path, "/w1_slave", sizeof(w1_path)-strlen(w1_path)); + + /* open file /sys/bus/w1/devices/28-xxxx/w1_slave to get temperature */ + if( (fd=open(w1_path, O_RDONLY)) < 0 ) + { + log_error("open %s error: %s\n", w1_path, strerror(errno)); + return -2; + } + + if(read(fd, buf, sizeof(buf)) < 0) + { + log_error("read %s error: %s\n", w1_path, strerror(errno)); + close(fd); + return -2; + } + + ptr = strstr(buf, "t="); + if( !ptr ) + { + log_error("ERROR: Can not get temperature\n"); + close(fd); + return -2; + } + + ptr+=2; + + /* use two bytes to save temperature value */ + byte = (uint8_t *)temp; + byte[1] = atoi(ptr)/1000; /* integer part */ + byte[0] = (atoi(ptr)%1000)/10; /* fractional part, two digits after */ + + close(fd); + return 0; +} diff --git a/apue/project_socket/src/ds18b20.h b/apue/project_socket/src/ds18b20.h new file mode 100644 index 0000000..b71ae1e --- /dev/null +++ b/apue/project_socket/src/ds18b20.h @@ -0,0 +1,31 @@ +/******************************************************************************** + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: ds18b20.h + * Description: This head file is get temperature by DS18B20 on RaspberryPi + * + * Version: 1.0.0(2020年04月15日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2020年04月15日 23时37分38秒" + * + ********************************************************************************/ + +#ifndef _DS18B20_H_ +#define _DS18B20_H_ + +#include <stdint.h> + +/* description: get temperature by DS18B20 on RaspberryPi + * return value: 0: Successfully <0: Failure + * output value: $temp: temperature value saved in two bytes: + * byte[0]: fractional part, two digits after + * byte[1]: integer part + */ +int ds18b20_get_temperature(uint16_t *temp); + +#define temper_integer(x) (((x)>>8) & 0xFF) +#define temper_fract(x) ( (x) & 0xFF) + +#endif /* ----- #ifndef _DS18B20_H_ ----- */ + diff --git a/apue/project_socket/src/logger.c b/apue/project_socket/src/logger.c new file mode 100644 index 0000000..66eef8f --- /dev/null +++ b/apue/project_socket/src/logger.c @@ -0,0 +1,165 @@ +/********************************************************************************* + * Copyright: (C) 2022 LingYun IoT System Studio + * All rights reserved. + * + * Filename: logger.c + * Description: This file + * + * Version: 1.0.0(15/04/22) + * Author: LingYun <lingyun@email.com> + * ChangeLog: 1, Release initial version on "15/04/22 10:38:49" + * + ********************************************************************************/ + +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <stdarg.h> +#include <string.h> +#include "logger.h" + +/* + * Program name variable is provided by the libc + */ +extern const char* __progname; +#define PROGRAM_NAME __progname + +/* + * Logger internal sctructure + */ +typedef struct logger_s { + FILE *fp; + int loglevel; + int use_stdout; +} logger_t; + +static struct logger_s g_logger; + +static const char* LOG_LEVELS[] = { + LOG_STRING_ERROR, + LOG_STRING_WARN, + LOG_STRING_INFO, + LOG_STRING_DEBUG +}; + +/* + * initial logger system + */ +int logger_init(char *filename, int loglevel) +{ + logger_term(); + + g_logger.loglevel = loglevel>LOG_LEVEL_MAX ? LOG_LEVEL_MAX : loglevel; + + /* $filename is NULL or match "stdout" will use standard output */ + if( !filename || !strcasecmp(filename, "stdout")) + { + g_logger.use_stdout = 1; + g_logger.fp = stderr; + } + else + { + g_logger.use_stdout = 0; + g_logger.fp = fopen(filename, "a"); + if( !g_logger.fp ) + { + fprintf(stderr, "Failed to open file '%s': %s", filename, strerror(errno)); + return -1; + } + } + + return 0; +} + +/* + * terminate logger system + */ +void logger_term(void) +{ + if( !g_logger.fp ) + { + return ; + } + + if( !g_logger.use_stdout ) + { + fclose(g_logger.fp); + } + + g_logger.use_stdout = 0; + g_logger.fp = NULL; + + return ; +} + + +/* + * Logging functions + */ +void log_generic(const int level, const char* format, va_list args) +{ + char message[256]; + struct tm* current_tm; + time_t time_now; + + vsprintf(message, format, args); + + time(&time_now); + current_tm = localtime(&time_now); + + int res = fprintf(g_logger.fp, + "<%s> %02i:%02i:%02i [%s] %s" + , PROGRAM_NAME + , current_tm->tm_hour + , current_tm->tm_min + , current_tm->tm_sec + , LOG_LEVELS[level] + , message ); + + fflush(g_logger.fp); +} + +void log_error(char *format, ...) +{ + va_list args; + va_start(args, format); + log_generic(LOG_LEVEL_ERROR, format, args); + va_end(args); +} + +void log_warn(char *format, ...) +{ + if (g_logger.loglevel < LOG_LEVEL_WARN) { + return; + } + + va_list args; + va_start(args, format); + log_generic(LOG_LEVEL_WARN, format, args); + va_end(args); +} + +void log_info(char *format, ...) +{ + if (g_logger.loglevel < LOG_LEVEL_INFO) { + return; + } + + va_list args; + va_start(args, format); + log_generic(LOG_LEVEL_INFO, format, args); + va_end(args); +} + +void log_debug(char *format, ...) +{ + if (g_logger.loglevel < LOG_LEVEL_DEBUG) { + return; + } + + va_list args; + va_start(args, format); + log_generic(LOG_LEVEL_DEBUG, format, args); + va_end(args); +} + diff --git a/apue/project_socket/src/logger.h b/apue/project_socket/src/logger.h new file mode 100644 index 0000000..095c942 --- /dev/null +++ b/apue/project_socket/src/logger.h @@ -0,0 +1,52 @@ +/******************************************************************************** + * Copyright: (C) 2022 LingYun IoT System Studio + * All rights reserved. + * + * Filename: logger.h + * Description: This head file + * + * Version: 1.0.0(15/04/22) + * Author: LingYun <lingyun@email.com> + * ChangeLog: 1, Release initial version on "15/04/22 10:38:02" + * + ********************************************************************************/ + +#ifndef _LOGGER_H_ +#define _LOGGER_H_ + +/* + * logger level + */ +enum +{ + LOG_LEVEL_ERROR, + LOG_LEVEL_WARN, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_MAX, +}; + +/* + * logger prefix string for different logging levels + */ +#define LOG_STRING_ERROR "ERROR" +#define LOG_STRING_WARN "WARN " +#define LOG_STRING_INFO "INFO " +#define LOG_STRING_DEBUG "DEBUG" + + +/* + * logger initial and terminate functions + */ +int logger_init(char *filename, int loglevel); +void logger_term(void); + +/* + * logging methods by levels + */ +void log_error(char* format, ...); +void log_warn(char* format, ...); +void log_info(char* format, ...); +void log_debug(char* format, ...); + +#endif /* ----- #ifndef _LOGGER_H_ ----- */ diff --git a/apue/project_socket/src/makefile b/apue/project_socket/src/makefile new file mode 100644 index 0000000..05877f5 --- /dev/null +++ b/apue/project_socket/src/makefile @@ -0,0 +1,29 @@ + +LIBNAME = booster + +PREFIX ?= `pwd`/../libs/install +CFLAGS += -I ${PREFIX}/include + +INCFILES = $(wildcard *.h) +SRCFILES = $(wildcard *.c) +OBJFILES = $(patsubst %.c,%.o,$(SRCFILES)) + +all: $(OBJFILES) + @${AR} -rcs lib${LIBNAME}.a ${OBJFILES} + +%.o : %.c + @$(CC) $(CFLAGS) -c $< + +clean: + @rm -f *.o + rm -rf lib${LIBNAME}.* + +distclean: clean + +install: all + install lib${LIBNAME}.* ${PREFIX}/lib + install ${INCFILES} ${PREFIX}/include + +uninstall: + rm -f ${PREFIX}/lib/lib${LIBNAME}.* + rm -f ${PREFIX}/include/${INCFILES} diff --git a/apue/project_socket/src/packet.c b/apue/project_socket/src/packet.c new file mode 100644 index 0000000..1f1d9f9 --- /dev/null +++ b/apue/project_socket/src/packet.c @@ -0,0 +1,67 @@ +/********************************************************************************* + * Copyright: (C) 2022 LingYun IoT System Studio + * All rights reserved. + * + * Filename: packet.c + * Description: This file is packet API functions + * + * Version: 1.0.0(18/04/22) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "18/04/22 16:30:25" + * + ********************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include "packet.h" +#include "logger.h" +#include "ds18b20.h" + +int get_devid(char *devid, int size, int sn) +{ + if( !devid || size<DEVID_LEN ) + { + log_error("Invalid input arugments\n"); + return -1; + } + + memset(devid, 0, size); + snprintf(devid, size, "rpi#%04d", sn); + return 0; +} + +int get_time(char *strtime, int size) +{ + time_t now = time(NULL); + struct tm *tnow = localtime(&now); + + if( !strtime || size<TIME_LEN ) + { + log_error("Invalid input arugments\n"); + return -1; + } + + snprintf(strtime, size, "%04d-%02d-%2d %02d:%02d:%02d", + tnow->tm_year+1900, tnow->tm_mon+1, tnow->tm_mday, + tnow->tm_hour, tnow->tm_min, tnow->tm_sec); + + return 0; +} + +int packet_string_pack(pack_info_t *pack_info, char *pack_buf, int size) +{ + if( !pack_info || !pack_buf || size<=0 ) + { + log_error("Invalid input arguments\n"); + return -1; + } + + snprintf(pack_buf, size, "%s|%s|%d.%d", pack_info->devid, pack_info->strtime, + temper_integer(pack_info->temper), temper_fract(pack_info->temper)); + + return strlen(pack_buf); +} + + diff --git a/apue/project_socket/src/packet.h b/apue/project_socket/src/packet.h new file mode 100644 index 0000000..1c18b84 --- /dev/null +++ b/apue/project_socket/src/packet.h @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright: (C) 2022 LingYun IoT System Studio + * All rights reserved. + * + * Filename: packet.h + * Description: This head file is packet API functions. + * + * Version: 1.0.0(18/04/22) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "18/04/22 16:24:40" + * + ********************************************************************************/ + + +#ifndef _PACKET_H_ +#define _PACKET_H_ + +#include <stdint.h> + +#define DEVID_LEN 16 +#define TIME_LEN 32 + +typedef struct pack_info_s +{ + char devid[DEVID_LEN]; /* device ID */ + char strtime[TIME_LEN]; /* sample time */ + uint16_t temper; /* sample temperature */ +} pack_info_t; + +/* packet function pointer type */ +typedef int (* pack_proc_t)(pack_info_t *pack_info, char *pack_buf, int size); + +/* description: get device ID + * input args: + * $devid : device ID string + * $size : device ID output buffer size + * $sn : serial number + * return value: <0: failure 0:ok + */ +extern int get_devid(char *devid, int size, int sn); + +/* description: get current system in format "YYYY-MM-DD HH:MM:SS" + * input args: + * $strtime: time string output buf + * $size : time string output buffer size + * return value: <0: failure 0:ok + */ +extern int get_time(char *strtime, int size); + +/* description: package a string packet in format "devid|time|temper" + * input args: + * $pack_info: packet data contains devid, time and temperature + * $pack_buf : packet output buffer + * $size : packet output buffer size + * return value: <0: failure >0: packet bytes + */ +extern int packet_string_pack(pack_info_t *pack_info, char *pack_buf, int size); + +#endif /* ----- #ifndef _PACKET_H_ ----- */ diff --git a/apue/project_socket/src/socket.c b/apue/project_socket/src/socket.c new file mode 100644 index 0000000..9de6d1a --- /dev/null +++ b/apue/project_socket/src/socket.c @@ -0,0 +1,465 @@ +/******************************************************************************** + * Copyright: (C) 2022 LingYun IoT System Studio + * All rights reserved. + * + * Filename: socket.c + * Description: This file is for socket API functions + * + * Version: 1.0.0(18/04/22) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "18/04/22 17:09:59" + * + ********************************************************************************/ +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <netdb.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/un.h> +#include <poll.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <sys/resource.h> + +#include "socket.h" +#include "logger.h" + +/* description: initial socket context + * input args: + * $sock: socket context pointer + * $host: connect server hostname for client mode, unused for server mode + * $port: connect server port for client mode or listen port for server mode + * return value: <0: failure 0:ok + */ +int socket_init(socket_ctx_t *sock, char *host, int port) +{ + if( !sock || !host || port<=0 ) + return -1; + + memset( sock, 0, sizeof(*sock) ); + sock->fd = -1; + strncpy(sock->host, host, HOSTNAME_LEN); + sock->port = port; +} + +/* description: close socket + * input args: + * $sock: socket context pointer + * return value: <0: failure 0:ok + */ +int socket_term(socket_ctx_t *sock) +{ + if( !sock ) + return -1; + + if( sock->fd > 0) + { + close(sock->fd); + sock->fd = -1; + } + + return 0; +} + +/* description: socket server start listen + * input args: + * $sock: socket context pointer + * return value: <0: failure 0:ok + */ +int socket_listen(socket_ctx_t *sock) +{ + int rv = 0; + struct sockaddr_in addr; + int backlog = 13; + + if( !sock ) + return -1; + +} + +/* description: socket check connected or not + * input args: + * $sock: socket context pointer + * return value: <0: failure 0:ok + */ +int socket_connect(socket_ctx_t *sock) +{ + int rv = 0; + int sockfd = 0; + struct sockaddr_in servaddr; + + if( !sock ) + return -1; + + socket_term(sock); + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(sockfd < 0) + { + log_error("create socket failure: %s\n", strerror(errno)); + return -1; + } + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family=AF_INET; + servaddr.sin_port = htons(sock->port); + inet_aton(sock->host, &servaddr.sin_addr); + + rv=connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); + if(rv < 0) + { + //log_error("connect to server[%s:%d] failure: %s\n", sock->host, sock->port, strerror(errno)); + close(sockfd); + return -2; + } + + log_info("Connect to server[%s:%d] on fd[%d] successfully!\n", sock->host, sock->port, sockfd); + + sock->fd = sockfd; + + return 0; +} + +/* description: send data from the socket + * input args: + * $sock : socket context pointer + * $data : socket send data + * $bytes: socket send data bytes + * return value: <0: failure 0:ok + */ +int socket_send(socket_ctx_t *sock, char *data, int bytes) +{ + int rv = 0; + int i = 0; + int left_bytes = bytes; + + if( !sock || !data || bytes<= 0 ) + return -1; + + while( left_bytes > 0 ) + { + rv=write(sock->fd, &data[i], left_bytes); + if( rv < 0 ) + { + log_info("socket[%d] write() failure: %s, close socket now\n", sock->fd, strerror(errno)); + socket_term(sock); + return -2; + } + else if( rv == left_bytes ) + { + log_info("socket send %d bytes data over\n", bytes); + return 0; + } + else + { + /* not send over this time, continue to send left data */ + i += rv; + left_bytes -= rv; + continue; + } + } +} + +/* description: receive data from the socket + * input args: + * $sock : socket context pointer + * $buf : socket receive data buffer + * $size : socket receive data buffer size + * $timeout: receive data time, <=0 will don't timeout + * return value: <0: failure 0:ok + */ +int socket_recv(socket_ctx_t *sock, char *buf, int size, int timeout) +{ + int rv = 0; + int i = 0; + fd_set rdset; + int maxfd; + + if( !sock || !buf || size<= 0 ) + return -1; + + memset(buf, 0, size); + + maxfd = sock->fd; + FD_ZERO(&rdset); + FD_SET(sock->fd, &rdset); + + if( timeout <= 0 ) /* no timeout */ + { + rv=select(maxfd+1, &rdset, NULL, NULL, NULL); + } + else + { + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + rv=select(maxfd+1, &rdset, NULL, NULL, &tv); + } + + if( rv < 0 ) + { + log_error("select() on socket[%d] got error: %s\n", sock->fd, strerror(errno)); + return -2; + } + else if( rv == 0 ) + { + log_error("select() on socket[%d] get timeout\n", sock->fd); + return 0; + } + else + { + rv = read(sock->fd, buf, size); + if( rv <= 0 ) + { + log_error("socket[%d] read() failure or got disconnected: %s, close socket now\n", sock->fd, strerror(errno)); + socket_term(sock); + return -2; + } + else + { + log_debug("socket[%d] receive %d bytes data\n", sock->fd, rv); + return rv; + } + } +} + + + +/*+-------------------------------------------------------------------+ + *| socket utils function | + *+-------------------------------------------------------------------+*/ + + +/* socket connected or not: <0: failure 0:ok */ +int sock_check_connect(int sockfd) +{ + struct tcp_info info; + int len=sizeof(info); + + if( sockfd < 0 ) + return -1; + + getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len); + + if( TCP_CLOSE==info.tcpi_state || TCP_CLOSING==info.tcpi_state || TCP_CLOSE_WAIT==info.tcpi_state ) + { + return -3; + } + + return -0; +} + +/* description: set socket listen port as reusable, fix port already used bug */ +int socket_set_reuseaddr(int sockfd) +{ + int opt = 1; + int len = sizeof (int); + + if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, (void *) &opt, len)) + { + log_error("Set socket[%d] option SO_REUSEADDR failed:%s\n", sockfd, strerror(errno)); + return -1; + } + log_debug("Set socket[%d] option SO_REUSEADDR ok\n", sockfd); + + return 0; +} + +/* set socket as non-block mode, common socket default work as block mode */ +int socket_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_debug("Set socket[%d] none blocking\n", sockfd); + return opts; +} + + +/* set socket receive and send buffer size in linux kernel space */ +int socket_set_buffer(int sockfd, int rsize, int ssize) +{ + int opt; + socklen_t optlen = sizeof(opt); + + if(sockfd < 0) + return -1; + + /* Get system default receive buffer size, Linux X86: 85K */ + if (getsockopt (sockfd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, &optlen)) + { + log_warn("getsockopt() get receive buffer failure: %s\n", strerror(errno)); + return -2; + } + + /* Only when current receive buffer size larger than the default one will change it */ + if(rsize > opt) + { + opt = (int) rsize; + if (setsockopt (sockfd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, optlen)) + { + log_warn("setsockopt() set receive buffer to %d failure: %s\n", opt, strerror(errno)); + return -2; + } + } + + /* Get system default send buffer size, Linux X86: 16K */ + if (getsockopt (sockfd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, &optlen)) + { + log_warn("getsockopt() get send buffer failure: %s\n", strerror(errno)); + return -3; + } + + /* Only when current receive buffer size larger than the default one will change it */ + if(ssize > opt) + { + opt = (int) ssize; + if (setsockopt (sockfd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, optlen)) + { + log_warn("setsockopt() set send buffer to %d failure: %s\n", opt, strerror(errno)); + return -3; + } + } + + log_info("Set socket[%d] RCVBUF size:%d SNDBUF size:%d\n", sockfd, rsize, ssize); + return 0; +} + +/* + * Enable socket SO_KEEPALIVE, if the connection disconnected, any system call on socket + * will return immediately and errno will be set to "WSAENOTCONN" + * + * keepalive is not program related, but socket related, * so if you have multiple sockets, + * you can handle keepalive for each of them separately. + * + * Reference: http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/ + */ +int socket_set_keepalive(int sockfd, int keepintvl, int keepcnt) +{ + int opt; + + if(sockfd < 0) + return -1; + + /* Enable the KEEPALIVE flag */ + opt = 1; + if (setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof (opt))) + { + log_warn("setsockopt() enable SO_KEEPALIVE failure: %s\n", strerror(errno)); + return -2; + } + + if(keepintvl || keepcnt) + { + /* + * The tcp_keepidle parameter specifies the interval between the last data packet sent + * (simple ACKs are not considered data) and the first keepalive probe; after the + * connection is marked to need keepalive, this counter is not used any further. + * ~ >: cat /proc/sys/net/ipv4/tcp_keepalive_time + * 7200 + */ + opt = 3; /* 3 seconds */ + if (setsockopt (sockfd, SOL_TCP, TCP_KEEPIDLE, (char *) &opt, sizeof (opt))) + { + log_error("setsockopt() set TCP_KEEPIDLE to %d seconds failure: %s\n", opt, strerror(errno)); + return -3; + } + + if((opt=keepintvl) > 0) + { + /* + * The tcp_keepintvl parameter specifies the interval between subsequential keepalive + * probes, regardless of what the connection has exchanged in the meantime. + * ~ >: cat /proc/sys/net/ipv4/tcp_keepalive_intvl + * 75 + */ + if (setsockopt (sockfd, SOL_TCP, TCP_KEEPINTVL, (char *) &opt, sizeof (opt))) + { + log_error("setsockopt() set TCP_KEEPINTVL to %d failure: %s\n", opt, strerror(errno)); + return -4; + } + } + + if((opt=keepcnt) > 0) + { + /* + * The TCP_KEEPCNT option specifies the maximum number of unacknowledged probes to + * send before considering the connection dead and notifying the application layer + * probes to be sent. The value of TCP_KEEPCNT is an integer value between 1 and n, + * where n is the value of the systemwide tcp_keepcnt parameter. + * ~ >: cat /proc/sys/net/ipv4/tcp_keepalive_probes + * 9 + */ + if (setsockopt (sockfd, SOL_TCP, TCP_KEEPCNT, (char *) &opt, sizeof (opt))) + { + log_error("setsockopt() set TCP_KEEPCNT to %d failure: %s\n", opt, strerror(errno)); + return -5; + } + } + } + + log_debug("Set socket[%d] KEEPINTVL:%d KEEPCNT:%d\n", sockfd, keepintvl, keepcnt); + return 0; +} + + +/* Set open file description count to max */ +void set_socket_rlimit(void) +{ + struct rlimit limit = {0}; + + getrlimit(RLIMIT_NOFILE, &limit ); + limit.rlim_cur = limit.rlim_max; + setrlimit(RLIMIT_NOFILE, &limit ); + + log_info("set socket open fd max count to %d\n", limit.rlim_max); +} + diff --git a/apue/project_socket/src/socket.h b/apue/project_socket/src/socket.h new file mode 100644 index 0000000..e23b850 --- /dev/null +++ b/apue/project_socket/src/socket.h @@ -0,0 +1,100 @@ +/******************************************************************************** + * Copyright: (C) 2022 LingYun IoT System Studio + * All rights reserved. + * + * Filename: socket.h + * Description: This head file is for socket API functions + * + * Version: 1.0.0(18/04/22) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "18/04/22 17:09:59" + * + ********************************************************************************/ + +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#define HOSTNAME_LEN 64 + +typedef struct socket_ctx_s +{ + char host[HOSTNAME_LEN]; /* CLIENT: Connect server hostname; SERVER: Unused */ + int port; /* CLIENT: Connect server port; SERVER: listen port */ + int fd; /* socket descriptor */ +} socket_ctx_t; + +/* description: initial socket context + * input args: + * $sock: socket context pointer + * $host: connect server hostname for client mode, unused for server mode + * $port: connect server port for client mode or listen port for server mode + * return value: <0: failure 0:ok + */ +extern int socket_init(socket_ctx_t *sock, char *host, int port); + +/* description: close socket + * input args: + * $sock: socket context pointer + * return value: <0: failure 0:ok + */ +extern int socket_term(socket_ctx_t *sock); + +/* description: socket server start listen + * input args: + * $sock: socket context pointer + * return value: <0: failure 0:ok + */ +extern int socket_listen(socket_ctx_t *sock); + +/* description: socket client connect to server + * input args: + * $sock: socket context pointer + * return value: <0: failure 0:ok + */ +extern int socket_connect(socket_ctx_t *sock); + +/* description: send data from the socket + * input args: + * $sock : socket context pointer + * $data : socket send data + * $bytes: socket send data bytes + * return value: <0: failure 0:ok + */ +extern int socket_send(socket_ctx_t *sock, char *data, int bytes); + +/* description: receive data from the socket + * input args: + * $sock : socket context pointer + * $buf : socket receive data buffer + * $size : socket receive data buffer size + * $timeout: receive data time, <=0 will don't timeout + * return value: <0: failure 0:ok + */ +#define TIMEOUT_NONE 0 +extern int socket_recv(socket_ctx_t *sock, char *buf, int size, int timeout); + +/*+-------------------------------------------------------------------+ + *| socket utils function | + *+-------------------------------------------------------------------+*/ + + +/* socket connected or not: <0: failure 0:ok */ +extern int sock_check_connect(int sockfd); + +/* description: set socket listen port as reusable, fix port already used bug */ +extern int socket_set_reuseaddr(int sockfd); + +/* set socket as non-block mode, common socket default work as block mode */ +extern int socket_set_nonblock(int sockfd); + +/* set socket receive and send buffer size in linux kernel space */ +extern int socket_set_buffer(int sockfd, int rsize, int ssize); + +/* set heartbeat keepalive */ +extern int socket_set_keepalive(int sockfd, int keepintvl, int keepcnt); + +/* Set open file description count to max */ +extern void set_socket_rlimit(void); + +#endif /* ----- #ifndef _SOCKET_H_ ----- */ + diff --git a/apue/project_socket/src/sqlite_blob.c b/apue/project_socket/src/sqlite_blob.c new file mode 100644 index 0000000..07a95f1 --- /dev/null +++ b/apue/project_socket/src/sqlite_blob.c @@ -0,0 +1,265 @@ +/******************************************************************************** + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: sqlite_blob.c + * Description: This library used to operate blob packet in sqlite database. + * + * Version: 1.0.0(2020年05月13日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2020年05月13日 12时14分23秒" + * + ********************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "sqlite_blob.h" +#include "logger.h" + +/* Blob packet table name */ +#define TABLE_NAME "PackTable" + +/* Use static global handler here in order to simplify the API, + * But it will make this library not thread safe + */ +static sqlite3 *s_clidb = NULL; + + +/* description: open or create sqlite database if not exist + * input args: + * $db_file: sqlite database file name + * return value: <0: failure 0:ok + * */ +int database_init(const char *db_file) +{ + char sql[SQL_COMMAND_LEN]={0}; + char *errmsg = NULL; + + if( !db_file ) + { + log_error("%s() Invalid input arguments\n", __func__); + return -1; + } + + /*+------------------------------------------+ + *| database already exist, just open it | + *+------------------------------------------+*/ + if( 0==access(db_file, F_OK) ) + { + if( SQLITE_OK != sqlite3_open(db_file, &s_clidb) ) + { + log_error("open database file '%s' failure\n", db_file); + return -2; + } + log_info("open database file '%s' ok\n", db_file); + return 0; + } + + /*+-----------------------------------------+ + *| database not exist, create and init it | + *+-----------------------------------------+*/ + + if( SQLITE_OK != sqlite3_open(db_file, &s_clidb) ) + { + log_error("create database file '%s' failure\n", db_file); + return -2; + } + + /* SQLite continues without syncing as soon as it has handed data off to the operating system */ + sqlite3_exec(s_clidb, "pragma synchronous = OFF; ", NULL, NULL, NULL); + + /* enable full auto vacuum, Auto increase/decrease */ + sqlite3_exec(s_clidb, "pragma auto_vacuum = 2 ; ", NULL, NULL, NULL); + + /* Create firehost table in the database */ + snprintf(sql, sizeof(sql), "CREATE TABLE %s(packet BLOB);", TABLE_NAME); + if( SQLITE_OK != sqlite3_exec(s_clidb, sql, NULL, NULL, &errmsg) ) + { + log_error("create data_table in database file '%s' failure: %s\n", db_file, errmsg); + sqlite3_free(errmsg); /* free errmsg */ + sqlite3_close(s_clidb); /* close databse */ + unlink(db_file); /* remove database file */ + } + + log_info("create and init database file '%s' ok\n", db_file); + return 0; +} + + +/* description: close sqlite database handler + * return value: none + */ +void database_term(void) +{ + log_warn("close sqlite database now\n"); + sqlite3_close(s_clidb); + + return ; +} + + +/* description: push a blob packet into database + * input args: + * $pack: blob packet data address + * $size: blob packet data bytes + * return value: <0: failure 0:ok + */ +int blobdb_push_packet(void *pack, int size) +{ + char sql[SQL_COMMAND_LEN]={0}; + int rv = 0; + char *errmsg = NULL; + sqlite3_stmt *stat = NULL; + + if( !pack || size<=0 ) + { + log_error("%s() Invalid input arguments\n", __func__); + return -1; + } + + if( ! s_clidb ) + { + log_error("sqlite database not opened\n"); + return -2; + } + + snprintf(sql, sizeof(sql), "insert into %s(packet) values(?)", TABLE_NAME); + rv = sqlite3_prepare_v2(s_clidb, sql, -1, &stat, NULL); + if(SQLITE_OK!=rv || !stat) + { + log_error("blob add sqlite3_prepare_v2 failure\n"); + rv = -2; + goto OUT; + } + + if( SQLITE_OK != sqlite3_bind_blob(stat, 1, pack, size, NULL) ) + { + log_error("blob add sqlite3_bind_blob failure\n"); + rv = -3; + goto OUT; + } + + rv = sqlite3_step(stat); + if( SQLITE_DONE!=rv && SQLITE_ROW!=rv ) + { + log_error("blob add sqlite3_step failure\n"); + rv = -4; + goto OUT; + } + +OUT: + sqlite3_finalize(stat); + + if( rv < 0 ) + log_error("add new blob packet into database failure, rv=%d\n", rv); + else + log_info("add new blob packet into database ok\n"); + + return rv; +} + + +/* description: pop the first blob packet from database + * input args: + * $pack: blob packet output buffer address + * $size: blob packet output buffer size + * $byte: blob packet bytes + * return value: <0: failure 0:ok + */ +int blobdb_pop_packet(void *pack, int size, int *bytes) +{ + char sql[SQL_COMMAND_LEN]={0}; + int rv = 0; + sqlite3_stmt *stat = NULL; + const void *blob_ptr; + + if( !pack || size<=0 ) + { + log_error("%s() Invalid input arguments\n", __func__); + return -1; + } + + if( ! s_clidb ) + { + log_error("sqlite database not opened\n"); + return -2; + } + + /* Only query the first packet record */ + snprintf(sql, sizeof(sql), "select packet from %s limit 0,1;", TABLE_NAME); + rv = sqlite3_prepare_v2(s_clidb, sql, -1, &stat, NULL); + if(SQLITE_OK!=rv || !stat) + { + log_error("firehost sqlite3_prepare_v2 failure\n"); + rv = -3; + goto out; + } + + rv = sqlite3_step(stat); + if( SQLITE_DONE!=rv && SQLITE_ROW!=rv ) + { + log_error("firehost sqlite3_step failure\n"); + rv = -5; + goto out; + } + + /* 1rd argument<0> means first segement is packet */ + blob_ptr = sqlite3_column_blob(stat, 0); + if( !blob_ptr ) + { + rv = -6; + goto out; + } + + *bytes = sqlite3_column_bytes(stat, 0); + + if( *bytes > size ) + { + log_error("blob packet bytes[%d] larger than bufsize[%d]\n", *bytes, size); + *bytes = 0; + rv = -1; + } + + memcpy(pack, blob_ptr, *bytes); + rv = 0; + +out: + sqlite3_finalize(stat); + return rv; +} + + +/* description: remove the first blob packet from database + * input args: none + * return value: <0: failure 0:ok + */ +int blobdb_del_packet(void) +{ + char sql[SQL_COMMAND_LEN]={0}; + char *errmsg = NULL; + + if( ! s_clidb ) + { + log_error("sqlite database not opened\n"); + return -2; + } + + /* remove packet from db */ + memset(sql, 0, sizeof(sql)); + snprintf(sql, sizeof(sql), "delete from %s limit 0,1;", TABLE_NAME); + if( SQLITE_OK != sqlite3_exec(s_clidb, sql, NULL, 0, &errmsg) ) + { + log_error("delete first blob packet from database failure: %s\n", errmsg); + sqlite3_free(errmsg); + return -2; + } + log_warn("delete first blob packet from database ok\n"); + + /* Vacuum the database */ + sqlite3_exec(s_clidb, "VACUUM;", NULL, 0, NULL); + + return 0; +} + diff --git a/apue/project_socket/src/sqlite_blob.h b/apue/project_socket/src/sqlite_blob.h new file mode 100644 index 0000000..953e50e --- /dev/null +++ b/apue/project_socket/src/sqlite_blob.h @@ -0,0 +1,60 @@ +/******************************************************************************** + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: sqlite_blob.h + * Description: This library used to operate blob packet in sqlite database. + * + * Version: 1.0.0(2020年05月13日) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "2020年05月13日 12时14分23秒" + * + ********************************************************************************/ +#ifndef _SQLITE_BLOB_H_ +#define _SQLITE_BLOB_H_ + +#include "sqlite3.h" + +#define SQL_COMMAND_LEN 256 + +/* description: open or create sqlite database if not exist + * input args: + * $db_file: sqlite database file name + * return value: <0: failure 0:ok + * */ +extern int database_init(const char *db_file); + + +/* description: close sqlite database handler + * return value: none + */ +extern void database_term(void); + + +/* description: push a blob packet into database + * input args: + * $pack: blob packet data address + * $size: blob packet data bytes + * return value: <0: failure 0:ok + */ +extern int blobdb_push_packet(void *pack, int size); + + +/* description: pop the first blob packet from database + * input args: + * $pack: blob packet output buffer address + * $size: blob packet output buffer size + * $byte: blob packet bytes + * return value: <0: failure 0:ok + */ +extern int blobdb_pop_packet(void *pack, int size, int *bytes); + + +/* description: remove the first blob packet from database + * input args: none + * return value: <0: failure 0:ok + */ +extern int blobdb_del_packet(void); + + +#endif /* ----- #ifndef _SQLITE_BLOB_H_ ----- */ diff --git a/apue/project_socket/src/util_proc.c b/apue/project_socket/src/util_proc.c new file mode 100644 index 0000000..039443d --- /dev/null +++ b/apue/project_socket/src/util_proc.c @@ -0,0 +1,433 @@ +/********************************************************************************* + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: util_proc.c + * Description: This file is the process API + * + * Version: 1.0.0(7/06/2020) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "7/06/2020 09:19:02 PM" + * + ********************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libgen.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pthread.h> + +#include "util_proc.h" +#include "logger.h" + +proc_signal_t g_signal={0}; + +void proc_default_sighandler(int sig) +{ + switch(sig) + { + case SIGINT: + log_warn("SIGINT - stopping\n"); + g_signal.stop = 1; + break; + + case SIGTERM: + log_warn("SIGTERM - stopping\n"); + g_signal.stop = 1; + break; + + case SIGSEGV: + log_warn("SIGSEGV - stopping\n"); +#if 0 + if(g_signal.stop) + exit(0); + + g_signal.stop = 1; +#endif + break; + + case SIGPIPE: + log_warn("SIGPIPE - warnning\n"); + break; + + default: + break; + } +} + + +/* install default signal process functions */ +void install_default_signal(void) +{ + struct sigaction sigact, sigign; + + log_info("Install default signal handler.\n"); + + /* Initialize the catch signal structure. */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = proc_default_sighandler; + + /* Setup the ignore signal. */ + sigemptyset(&sigign.sa_mask); + sigign.sa_flags = 0; + sigign.sa_handler = SIG_IGN; + + sigaction(SIGTERM, &sigact, 0); /* catch terminate signal "kill" command */ + sigaction(SIGINT, &sigact, 0); /* catch interrupt signal CTRL+C */ + //sigaction(SIGSEGV, &sigact, 0); /* catch segmentation faults */ + sigaction(SIGPIPE, &sigact, 0); /* catch broken pipe */ +#if 0 + sigaction(SIGCHLD, &sigact, 0); /* catch child process return */ + sigaction(SIGUSR2, &sigact, 0); /* catch USER signal */ +#endif +} + + +/* **************************************************************************** + * FunctionName: daemonize + * Description : Set the programe runs as daemon in background + * Inputs : nodir: DON'T change the work directory to / : 1:NoChange 0:Change + * noclose: close the opened file descrtipion or not 1:Noclose 0:Close + * Output : NONE + * Return : NONE + * *****************************************************************************/ +void daemonize(int nochdir, int noclose) +{ + int rv, fd; + int i; + + /* already a daemon */ + if (1 == getppid()) + return; + + /* fork error */ + rv = fork(); + if (rv < 0) exit(1); + + /* parent process exit */ + if (rv > 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: check_set_program_running + * Description : check program already running or not, if not then run it and + * record pid into $pidfile + * Inputs : daemon: set program running in daemon or not + * pid_file:The record PID file path + * Output : NONE + * Return : 0: Record successfully Else: Failure + * *****************************************************************************/ + +int check_set_program_running(int daemon, char *pidfile) +{ + if( !pidfile ) + return 0; + + if( check_daemon_running(pidfile) ) + { + log_error("Program already running, process exit now"); + return -1; + } + + if( daemon ) + { + if( set_daemon_running(pidfile) < 0 ) + { + log_error("set program running as daemon failure\n"); + return -2; + } + } + else + { + if( record_daemon_pid(pidfile) < 0 ) + { + log_error("record program running PID failure\n"); + return -3; + } + } + + return 0; +} + + + +/* **************************************************************************** + * 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_error("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_debug("Record PID<%u> to file %s.\n", getpid(), pid_file); + } + else + { + log_error("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_error("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 rv = -1; + struct stat fStatBuf; + + rv = stat(pid_file, &fStatBuf); + if (0 == rv) + { + 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 ((rv = kill(pid, 0)) == 0) + { + printf("Program with PID[%d] seems running.\n", pid); + return 1; + } + else /* Send signal to the old process get no reply. */ + { + printf("Program with PID[%d] seems exit.\n", pid); + remove(pid_file); + return 0; + } + } + else if (0 == pid) + { + printf("Can not read program PID form record file.\n"); + remove(pid_file); + return 0; + } + else /* Read pid from file "pid_file" failure */ + { + printf("Read record file \"%s\" failure, maybe program still running.\n", pid_file); + return 1; + } + } + + return 0; +} + +/* **************************************************************************** + * FunctionName: stop_daemon_running + * Description : Stop the daemon program running + * Inputs : pid_file: The record running daemon program PID + * Output : NONE + * Return : 1: The daemon program alread running 0: Not running + * *****************************************************************************/ +int stop_daemon_running(const char *pid_file) +{ + pid_t pid = -1; + struct stat fStatBuf; + + if ( stat(pid_file, &fStatBuf) < 0) + return 0; + + printf("PID record file \"%s\" exist.\n", pid_file); + pid = get_daemon_pid(pid_file); + if (pid > 0) /* Process pid exist */ + { + while ( (kill(pid, 0) ) == 0) + { + kill(pid, SIGTERM); + sleep(1); + } + + remove(pid_file); + } + + return 0; +} + +/* **************************************************************************** + * FunctionName: set_daemon_running + * Description : Set the programe running as daemon if it's not running and record + * its PID to the pid_file. + * Inputs : pid_file: The record running daemon program PID + * Output : NONE + * Return : 0: Successfully. 1: Failure + * *****************************************************************************/ +int set_daemon_running(const char *pid_file) +{ + daemonize(0, 1); + log_info("Program running as daemon [PID:%d].\n", getpid()); + + if (record_daemon_pid(pid_file) < 0) + { + log_error("Record PID to file \"%s\" failure.\n", pid_file); + return -2; + } + + return 0; +} + +/* start a new thread to run $thread_workbody point function */ +int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg) +{ + int rv = 0; + pthread_t tid; + + pthread_attr_t thread_attr; + + /* Initialize the thread attribute */ + rv = pthread_attr_init(&thread_attr); + if(rv) + return -1; + +#if 0 + /* Set the stack size of the thread */ + rv = pthread_attr_setstacksize(&thread_attr, 120 * 1024); + if(rv) + goto CleanUp; +#endif + + /* Set thread to detached state:Don`t need pthread_join */ + rv = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + if(rv) + goto CleanUp; + + /* Create the thread */ + rv = pthread_create(&tid, &thread_attr, thread_workbody, thread_arg); + if(rv) + goto CleanUp; + +CleanUp: + + + if( thread_id ) + { + if( rv ) + *thread_id = 0; + else + *thread_id = tid; + } + + /* Destroy the attributes of thread */ + pthread_attr_destroy(&thread_attr); + return rv; +} + + +/* excute a linux command by system() */ +void exec_system_cmd(const char *format, ...) +{ + char cmd[256]; + va_list args; + + memset(cmd, 0, sizeof(cmd)); + + va_start(args, format); + vsnprintf(cmd, sizeof(cmd), format, args); + va_end(args); + + system(cmd); +} + + diff --git a/apue/project_socket/src/util_proc.h b/apue/project_socket/src/util_proc.h new file mode 100644 index 0000000..b52692f --- /dev/null +++ b/apue/project_socket/src/util_proc.h @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: util_proc.h + * Description: This head file is for Linux process/thread API + * + * Version: 1.0.0(7/06/2012~) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "7/06/2012 09:21:33 PM" + * + ********************************************************************************/ + +#ifndef __UTIL_PROC_H_ +#define __UTIL_PROC_H_ + +#include <signal.h> + +#define PID_ASCII_SIZE 11 + +typedef struct proc_signal_s +{ + int signal; + unsigned stop; /* 0: Not term 1: Stop */ +} proc_signal_t; + +typedef void *(* thread_body_t) (void *thread_arg); + +extern proc_signal_t g_signal; + +/* install default signal process functions */ +extern void install_default_signal(void); + +/* excute a linux command by system() */ +extern void exec_system_cmd(const char *format, ...); + +/* check program already running or not, if not then run it and record pid into $pidfile */ +extern int check_set_program_running(int daemon, char *pidfile); + +/* stop program running from $pid_file */ +extern int stop_daemon_running(const char *pid_file); + +/* my implementation for set program running in daemon */ +extern void daemonize(int nochdir, int noclose); + +/* start a new thread to run $thread_workbody point function */ +extern int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg); + +/* +---------------------+ + * | Low level API | + * +---------------------+*/ + +/* record proces ID into $pid_file */ +extern int record_daemon_pid(const char *pid_file); + +/* get daemon process ID from $pid_file */ +extern pid_t get_daemon_pid(const char *pid_file); + +/* check program already running or not from $pid_file */ +extern int check_daemon_running(const char *pid_file); + +/* set program daemon running and record pid in $pid_file */ +extern int set_daemon_running(const char *pid_file); + +#endif diff --git a/apue/project_socket/src/util_time.h b/apue/project_socket/src/util_time.h new file mode 100644 index 0000000..0f8dbcc --- /dev/null +++ b/apue/project_socket/src/util_time.h @@ -0,0 +1,186 @@ +/******************************************************************************** + * Copyright: (C) 2020 LingYun IoT System Studio + * All rights reserved. + * + * Filename: util_time.h + * Description: This head file is system time, timer API + * + * Version: 1.0.0(07/23/2020) + * Author: Guo Wenxue <guowenxue@gmail.com> + * ChangeLog: 1, Release initial version on "07/23/2020 07:46:37 AM" + * + ********************************************************************************/ +#ifndef __UTIL_TIME_H_ +#define __UTIL_TIME_H_ + +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> + +#include <linux/rtc.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> + + +typedef struct date_time_s +{ + int year; + int month; + int day; + int hour; + int minute; + int second; + int dayofweek; +} date_time_t; + +/* sleep for micro second */ +static inline void msleep(unsigned long ms) +{ + struct timespec timeout; + unsigned long tmp; + + timeout.tv_sec = ms / 1000; + if (timeout.tv_sec == 0) + { + tmp = ms * 10000; + timeout.tv_nsec = tmp * 100; + } + else + { + timeout.tv_nsec = 0; + } + + nanosleep(&timeout, 0); +} + +/* call gettimeofday() to get current micro second */ +static inline unsigned long time_now() +{ + struct timeval now; + + gettimeofday(&now, 0); + + return (now.tv_sec*1000) + (now.tv_usec/1000); +} + +/* timep has elapsed since $start, unit as micro second*/ +static inline uint32_t time_elapsed(uint32_t start) +{ + uint32_t current = time_now(); + + if(current >= start) + return current-start; + else + return current+0xFFFFFFFF-start; +} + +/* call gettimeofday() to get current micro second */ +static inline unsigned long time_second() +{ + struct timeval now; + + gettimeofday(&now, 0); + return now.tv_sec; +} + +/* timep has elapsed since $start, unit as micro second*/ +static inline uint32_t seconds_elapsed(uint32_t start) +{ + uint32_t current = time_second(); + + if(current >= start) + return current-start; + else + return current+0xFFFFFFFF-start; +} + +/* +* These inlines deal with timer wrapping correctly. You are +* strongly encouraged to use them +* 1. Because people otherwise forget +* 2. Because if the timer wrap changes in future you won't have to +* alter your driver code. +* +* time_after(a,b) returns true if the time a is after time b. +* +* Do this with "<0" and ">=0" to only test the sign of the result. A +* good compiler would generate better code (and a really good compiler +* wouldn't care). Gcc is currently neither. +*/ + +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +#define time_after(a,b) \ +(typecheck(unsigned long, a) && typecheck(unsigned long, b) && ((long)(b) - (long)(a) < 0)) +#define time_before(a,b) time_after(b,a) + +#define time_after_eq(a,b) \ +(typecheck(unsigned long, a) && typecheck(unsigned long, b) && ((long)(a) - (long)(b) >= 0)) +#define time_before_eq(a,b) time_after_eq(b,a) + +/* Same as above, but does so with platform independent 64bit types. + * These must be used when utilizing jiffies_64 (i.e. return value of + * get_jiffies_64() */ +#define time_after64(a,b) \ + (typecheck(__u64, a) && typecheck(__u64, b) && ((__s64)(b) - (__s64)(a) < 0)) +#define time_before64(a,b) time_after64(b,a) + +#define time_after_eq64(a,b) \ + (typecheck(__u64, a) && typecheck(__u64, b) && ((__s64)(a) - (__s64)(b) >= 0)) +#define time_before_eq64(a,b) time_after_eq64(b,a) + + +static inline void get_sys_time(date_time_t *date) +{ + time_t now = time(NULL); + struct tm *tnow = localtime(&now); + + memset(date, 0, sizeof(*date)); + date->year = 1900 + tnow->tm_year; + date->month = 1 + tnow->tm_mon; + date->day = tnow->tm_mday; + + date->hour = tnow->tm_hour; + date->minute = tnow->tm_min; + date->second = tnow->tm_sec; + date->dayofweek = tnow->tm_wday; + return; +} + +static inline int get_rtc_time(date_time_t *date) +{ + int rv, fd = -1; + struct rtc_time rtc_tm; + + memset(date, 0, sizeof(*date)); + + if ((fd=open("/dev/rtc0", O_RDONLY)) < 0) + return -1; + + if((rv=ioctl(fd, RTC_RD_TIME, &rtc_tm)) < 0) + return -2; + + date->year = 1900 + rtc_tm.tm_year; + date->month = 1 + rtc_tm.tm_mon; + date->day = rtc_tm.tm_mday; + + date->hour = rtc_tm.tm_hour; + date->minute = rtc_tm.tm_min; + date->second = rtc_tm.tm_sec; + date->dayofweek = rtc_tm.tm_wday; + + close(fd); + + return 0; +} + +#endif -- Gitblit v1.9.1