New file |
| | |
| | | #!/bin/bash |
| | | |
| | | make |
| | | ./main/client_main -i 127.0.0.1 -p 8888 -I 30 -d |
New file |
| | |
| | | #!/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 |
New file |
| | |
| | | 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 |
New file |
| | |
| | | #!/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 |
New file |
| | |
| | | #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; |
| | | } |
New file |
| | |
| | | |
| | | 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 |
New file |
| | |
| | | #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);
|
| | | }
|
| | |
|
New file |
| | |
| | | |
| | | 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 |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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; |
| | | } |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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); |
| | | } |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
New file |
| | |
| | | |
| | | 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} |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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); |
| | | } |
| | | |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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); |
| | | } |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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; |
| | | } |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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); |
| | | } |
| | | |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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 |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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 |