| /** |
| * Copyright (c) 2014 Quantenna Communications, Inc. |
| * All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| **/ |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <netinet/in.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <ctype.h> |
| #include <arpa/inet.h> |
| |
| #define QEVT_DEFAULT_PORT 3490 |
| #define QEVT_RX_BUF_SIZE 1024 |
| #define QEVT_CON_RETRY_DELAY 5U |
| #define QEVT_MAX_LOG_SIZE 256 |
| #define QEVT_ID_LEN 4 |
| #define ENABLE_CONFIG_CMD 1 |
| #define ENABLE_CONFIG_EVENT_ID 2 |
| |
| #define MAX_VERSION_LEN 5 |
| #define QEVT_CLIENT_VERSION "v1.10" |
| #define QEVT_CONFIG "QEVT_CONFIG" |
| #define QEVT_CONFIG_RESET QEVT_CONFIG"_RESET" |
| #define QEVT_VERSION "QEVT_VERSION" |
| #define QEVT_DEFAULT_CONFIG "WPACTRL3:-" |
| #define QEVT_CONFIG_EVENT_ID "QEVT_CONFIG_EVENT_ID" |
| #define QEVT_DEFAULT_CONFIG_EVENT_ID "default:+" |
| |
| char fpath[32] = "/tmp"; |
| const char * logname = "qtn5g.log"; |
| |
| char fpath1[32] = "/tmp"; |
| const char * log1name = "qtn5g.log.1"; |
| |
| int log_size = 5 * 1024; |
| char * log_buf = NULL; |
| |
| struct qevt_client_config { |
| struct sockaddr_in dest; |
| struct in_addr ip_addr; |
| int client_socket; |
| uint16_t port; |
| int fd; |
| char rxbuffer[QEVT_RX_BUF_SIZE + 1]; |
| char *qevt_config_cmd; |
| }; |
| |
| static int qevt_write_log(int * fd, void * buf, int len) |
| { |
| off_t file_off; |
| int bak_fd, log_bytes; |
| int log_fd = *fd; |
| |
| if (log_fd > 0) |
| close(log_fd); |
| |
| log_fd = open(fpath, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP); |
| |
| if (log_fd < 0) { |
| perror("fail to open qtn5g.log\n"); |
| return log_fd; |
| } |
| |
| file_off = lseek(log_fd, 0, SEEK_END); |
| |
| if ((int)file_off + len > log_size) { |
| file_off = lseek(log_fd, 0, SEEK_SET); |
| |
| log_bytes = read(log_fd, log_buf, log_size); |
| if (log_bytes < 0 ) { |
| perror("read error in qtn5g.log\n"); |
| return log_bytes; |
| } |
| close(log_fd); |
| *fd = 0; |
| |
| bak_fd = open(fpath1, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP); |
| if (bak_fd >= 0) { |
| if (log_bytes != write(bak_fd, log_buf, log_bytes)) { |
| perror ("fail to write qtn5g.log.1\n"); |
| } |
| close (bak_fd); |
| } else |
| perror("fail to open qtn5g.log.1\n"); |
| |
| log_fd = open(fpath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP); |
| |
| if (log_fd < 0) { |
| perror("fail to open qtn5g.log after change log\n"); |
| return log_fd; |
| } |
| |
| *fd = log_fd; |
| } |
| |
| log_bytes = write(log_fd, buf, len); |
| |
| return log_bytes; |
| |
| } |
| |
| static int qevt_send_host(struct qevt_client_config *const cfg, const char *msg) |
| { |
| int sent_bytes = 0; |
| int ret; |
| |
| do { |
| ret = send(cfg->client_socket, msg + sent_bytes, strlen(msg) - sent_bytes, 0); |
| |
| if (ret > 0) { |
| sent_bytes += ret; |
| } else if (errno == EINTR) { |
| continue; |
| } else { |
| break; |
| } |
| } while (sent_bytes < strlen(msg)); |
| |
| if (ret <= 0) { |
| fprintf(stderr, "%s: failure sending a message to event server\n", __func__); |
| } |
| return (ret > 0); |
| } |
| |
| static int qevt_receive(struct qevt_client_config *const cfg) |
| { |
| int received_bytes; |
| |
| do { |
| received_bytes = recv(cfg->client_socket, cfg->rxbuffer, sizeof(cfg->rxbuffer) - 1, 0); |
| |
| if (received_bytes > 0) { |
| cfg->rxbuffer[received_bytes] = '\0'; |
| } else if (received_bytes == 0) { |
| printf("Connection closed\n"); |
| break; |
| } else if (errno != EINTR && errno != ECONNREFUSED) { |
| perror("Receive failed"); |
| break; |
| } |
| } while ((received_bytes < 0) && (errno == EINTR)); |
| |
| return received_bytes; |
| } |
| |
| static char * qevt_config_cmd(struct qevt_client_config *const cfg, const int config_type) |
| { |
| char *msg; |
| char *nl; |
| |
| /* first, clear to initial settings */ |
| if (!qevt_send_host(cfg, QEVT_CONFIG_RESET"\n")) { |
| return NULL; |
| } |
| if (qevt_receive(cfg) <= 0) { |
| return NULL; |
| } |
| |
| /* then send config command */ |
| if (!qevt_send_host(cfg, cfg->qevt_config_cmd)) { |
| return NULL; |
| } |
| if (qevt_receive(cfg) <= 0) { |
| return NULL; |
| } |
| |
| msg = strstr(cfg->rxbuffer, (config_type != ENABLE_CONFIG_CMD) ? |
| QEVT_CONFIG_EVENT_ID" " : QEVT_CONFIG" "); |
| if (msg) { |
| msg = strstr(msg, " "); |
| } |
| if (msg && (nl = strstr(++msg, "\n"))) { |
| *nl = 0; |
| } |
| |
| return msg; |
| } |
| |
| static int qevt_check_version(struct qevt_client_config *const cfg, char** report, const int config_type) |
| { |
| char *version; |
| char *nl; |
| const char *cmd = QEVT_VERSION" "QEVT_CLIENT_VERSION; |
| |
| if (report) { |
| *report = "UNKNOWN"; |
| } |
| |
| if (!qevt_send_host(cfg, cmd) || (qevt_receive(cfg) < 0)) { |
| return 0; |
| } |
| |
| version = strstr(cfg->rxbuffer, QEVT_VERSION); |
| if (!version) { |
| return 0; |
| } |
| |
| version += sizeof(QEVT_VERSION) - 1; |
| version = strstr(version, "v"); |
| if (!version) { |
| return 0; |
| } |
| |
| nl = strstr(version, "\n"); |
| if (nl) { |
| *nl = 0; |
| } |
| |
| if (report) { |
| *report = version; |
| } |
| |
| if ((strcmp(version, QEVT_CLIENT_VERSION) < 0) && |
| config_type == ENABLE_CONFIG_EVENT_ID) { |
| printf("qevt_server[version %s] does not support -e option\n", version); |
| return 0; |
| } |
| |
| return (strcmp(QEVT_CLIENT_VERSION, version) >= 0); |
| } |
| |
| static void qevt_receiving_loop(struct qevt_client_config *const cfg) |
| { |
| int received_bytes; |
| char *buffer = cfg->rxbuffer; |
| |
| for (;;) { |
| received_bytes = qevt_receive(cfg); |
| |
| if (received_bytes <= 0) { |
| break; |
| } |
| if (received_bytes != qevt_write_log(&(cfg->fd), (void *)buffer, received_bytes)) { |
| fprintf(stderr, "write log fail\n"); |
| } |
| printf("%s", buffer); |
| } |
| } |
| |
| static int qevt_client_init(struct qevt_client_config *const cfg) |
| { |
| if (cfg->client_socket >= 0) |
| close(cfg->client_socket); |
| |
| cfg->client_socket = socket(AF_INET, SOCK_STREAM, 0); |
| if (cfg->client_socket < 0) { |
| perror("Failed to create client socket"); |
| return -1; |
| } |
| |
| memset(&cfg->dest, 0, sizeof(cfg->dest)); |
| cfg->dest.sin_family = AF_INET; |
| cfg->dest.sin_port = htons(cfg->port); |
| cfg->dest.sin_addr = cfg->ip_addr; |
| return 0; |
| } |
| |
| static int qevt_connected_to_server(const struct qevt_client_config *const cfg) |
| { |
| fd_set fdset; |
| struct timeval timeout = {.tv_sec = QEVT_CON_RETRY_DELAY, .tv_usec = 0}; |
| int connected = 0; |
| |
| FD_ZERO(&fdset); |
| FD_SET(cfg->client_socket, &fdset); |
| |
| if (select(cfg->client_socket + 1, NULL, &fdset, NULL, &timeout) == 1) { |
| int so_error; |
| socklen_t so_len = sizeof(so_error); |
| |
| getsockopt(cfg->client_socket, SOL_SOCKET, SO_ERROR, &so_error, &so_len); |
| if (!so_error) { |
| connected = 1; |
| } |
| } |
| |
| return connected; |
| } |
| |
| static void qevt_client_connect(struct qevt_client_config *const cfg) |
| { |
| int ret; |
| int connected = 0; |
| |
| while (!connected) { |
| fcntl(cfg->client_socket, F_SETFL, O_NONBLOCK); |
| ret = connect(cfg->client_socket, (struct sockaddr *)&cfg->dest, |
| sizeof(struct sockaddr)); |
| |
| if (ret < 0) { |
| switch (errno) { |
| case EINPROGRESS: |
| connected = qevt_connected_to_server(cfg); |
| if (connected) |
| break; |
| if (qevt_client_init(cfg) < 0) { |
| fprintf(stderr, "fail to create client\n"); |
| } |
| /* Fall through */ |
| case ECONNREFUSED: |
| case ETIMEDOUT: |
| fprintf(stderr, |
| "Cannot connect to the server. Trying again in %u secs.\n", |
| QEVT_CON_RETRY_DELAY); |
| sleep(QEVT_CON_RETRY_DELAY); |
| break; |
| case EINTR: |
| break; |
| default: |
| perror("Cannot connect"); |
| } |
| } |
| } |
| |
| fcntl(cfg->client_socket, F_SETFL, fcntl(cfg->client_socket, F_GETFL, 0) & ~O_NONBLOCK); |
| printf("Connection established\n"); |
| } |
| |
| static void qevt_usage(char *argv[]) |
| { |
| printf("Usage: %s [option]\n\t-h <host ip addr>\n\t-p <host ip port>" |
| "\n\t-d <file directory>\n\t-k <file size, unit k>\n\t-c <qevt config>\n\t" |
| "-e <event_id:->\n\t <default:+/-event id:-/+ ...>\n\t " |
| "<default:+/-[event_range_start:event_range_end]:-/+>\n\n\t" |
| "* -c and -e are mutually exclusive\n", argv[0]); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| static struct qevt_client_config client_cfg; |
| uint8_t config_type = 0; |
| int ch; |
| int status = EXIT_FAILURE; |
| char ip_addr[] = "255.255.255.255"; |
| |
| memset(&client_cfg ,0 ,sizeof(struct qevt_client_config)); |
| client_cfg.port = QEVT_DEFAULT_PORT; |
| |
| while ((ch = getopt(argc, argv, "h:p:d:k:c::e::")) != -1) { |
| switch (ch) { |
| case 'h': |
| strncpy(ip_addr, optarg, sizeof(ip_addr)); |
| ip_addr[sizeof(ip_addr) -1] = '\0'; |
| if (!inet_aton(optarg, &client_cfg.ip_addr)) { |
| fprintf(stderr, "IP address specified is not valid\n"); |
| goto exit; |
| } |
| break; |
| case 'p': |
| client_cfg.port = atoi(optarg); |
| break; |
| case 'd': |
| if ((int)strlen(optarg) > ((int)sizeof(fpath) - (int)strlen(log1name) - 2)) { |
| printf("Directory path length should not exceed %d\n", |
| ((int)sizeof(fpath) - (int)strlen(log1name) -2)); |
| goto exit; |
| } |
| |
| strncpy(fpath,optarg, strlen(optarg)); |
| strncpy(fpath1,optarg,strlen(optarg)); |
| break; |
| case 'k': |
| if (atoi(optarg) <= QEVT_MAX_LOG_SIZE) { |
| log_size = atoi(optarg) * 1024; |
| } else { |
| fprintf(stderr, "Log size cannot be larger than %d\n", |
| QEVT_MAX_LOG_SIZE); |
| goto exit; |
| } |
| break; |
| case 'c': |
| if (config_type) { |
| printf("Option -c an -e cannot be used together\n"); |
| goto exit; |
| } |
| |
| config_type = ENABLE_CONFIG_CMD; |
| if (!optarg) |
| optarg = QEVT_DEFAULT_CONFIG; |
| /* calculate the space needed (include extra for space and '\n') */ |
| client_cfg.qevt_config_cmd = malloc(sizeof(QEVT_CONFIG) + |
| strlen(optarg) + 4); |
| |
| if (client_cfg.qevt_config_cmd) { |
| sprintf(client_cfg.qevt_config_cmd, QEVT_CONFIG" %s\n", |
| optarg); |
| } else { |
| fprintf(stderr, "fail to malloc memory %u bytes\n", |
| (unsigned int)(sizeof(QEVT_CONFIG) + |
| strlen(optarg) + 4)); |
| goto exit; |
| } |
| break; |
| case 'e': |
| if (config_type) { |
| printf("Option -c an -e cannot be used together\n"); |
| goto exit; |
| } |
| |
| config_type = ENABLE_CONFIG_EVENT_ID; |
| if (!optarg) |
| optarg = QEVT_DEFAULT_CONFIG_EVENT_ID; |
| |
| /* calculate the space needed (include extra for space and '\n') */ |
| client_cfg.qevt_config_cmd = malloc(sizeof(QEVT_CONFIG_EVENT_ID) |
| + strlen(optarg) + 4); |
| if (client_cfg.qevt_config_cmd) { |
| sprintf(client_cfg.qevt_config_cmd, |
| QEVT_CONFIG_EVENT_ID" %s", optarg); |
| } else { |
| fprintf(stderr, "Failed to allocate memory for %u bytes\n", |
| (unsigned int)(sizeof(QEVT_CONFIG_EVENT_ID) + |
| strlen(optarg) + 4)); |
| goto exit; |
| } |
| break; |
| default: |
| qevt_usage(argv); |
| goto exit; |
| } |
| } |
| |
| if (!strncmp("255.255.255.255", ip_addr, strlen(ip_addr))) { |
| fprintf(stderr, "please select a valid ip address\n"); |
| goto exit; |
| } |
| |
| strncat(fpath, "/",1); |
| strncat(fpath,logname,strlen(logname)); |
| strncat(fpath1, "/",1); |
| strncat(fpath1,log1name,strlen(log1name)); |
| |
| client_cfg.client_socket = -1; |
| |
| if (!client_cfg.qevt_config_cmd) { |
| printf("Not enough options provided, either -c or -e option is required\n"); |
| goto exit; |
| } |
| |
| if (qevt_client_init(&client_cfg) < 0) { |
| goto exit; |
| } |
| |
| log_buf = malloc(log_size); |
| |
| if(log_buf == NULL) { |
| fprintf(stderr, "fail to malloc memory %d bytes\n", log_size); |
| goto exit; |
| } |
| |
| for (;;) { |
| char *report = NULL; |
| qevt_client_connect(&client_cfg); |
| |
| if (!qevt_check_version(&client_cfg, &report, config_type)) { |
| fprintf(stderr, "incompatible client version '"QEVT_CLIENT_VERSION |
| "'/server version '%s'\n", report); |
| goto exit; |
| } |
| |
| if ((report = qevt_config_cmd(&client_cfg, config_type))) { |
| printf("Server configuration '%s'\n", report); |
| } else { |
| fprintf(stderr, "unable to set/get config\n"); |
| goto exit; |
| } |
| |
| qevt_receiving_loop(&client_cfg); |
| qevt_client_init(&client_cfg); |
| } |
| /* |
| * This point is only reached if the above loop exits (which it should not currently). |
| * In case clean exit is added in future, exit with success status. |
| */ |
| status = EXIT_SUCCESS; |
| |
| exit: |
| if (log_buf) { |
| free(log_buf); |
| } |
| if (client_cfg.qevt_config_cmd) { |
| free(client_cfg.qevt_config_cmd); |
| } |
| return status; |
| } |
| |