diff --git a/Socket_CAN/socketcan0 b/Socket_CAN/socketcan0 new file mode 100755 index 0000000..8f28a6d --- /dev/null +++ b/Socket_CAN/socketcan0 @@ -0,0 +1,4 @@ +#!/bin/sh + +ip link set can0 type can bitrate 100000 triple-sampling on +ifconfig can0 up diff --git a/Socket_snippet/Makefile b/Socket_snippet/Makefile new file mode 100644 index 0000000..e67b9e9 --- /dev/null +++ b/Socket_snippet/Makefile @@ -0,0 +1,45 @@ +# run `make DEF=...` to add extra defines +PROGRAM := socket +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lusefull_macros -lm -L/usr/local/lib/ +SRCS := $(wildcard *.c) +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +OBJDIR := mk +CFLAGS += -O3 -Wno-trampolines -std=gnu99 +OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) +DEPS := $(OBJS:.o=.d) +CC = gcc +#CXX = g++ + + +all : $(PROGRAM) + +debug: CFLAGS += -DEBUG -Werror -Wall -Wextra +debug: all + +$(OBJS): $(OBJDIR) + +$(PROGRAM) : $(OBJS) + @echo -e "\t\tLD $(PROGRAM)" + $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + +$(OBJDIR): + mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) $< -MD -c $(CFLAGS) $(DEFINES) -o $@ + +clean: + @echo -e "\t\tCLEAN" + @rm -f $(OBJS) $(DEPS) + @rmdir $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/Socket_snippet/Readme.md b/Socket_snippet/Readme.md new file mode 100644 index 0000000..3af0afd --- /dev/null +++ b/Socket_snippet/Readme.md @@ -0,0 +1,7 @@ +Socket server and client snippet +================================ + +This snippet allows to create some utilities than can be run both in client or server mode. +The sockets are **local** TCP or UNIX sockets. Server-side polling use `poll()`. + +The pieces of user code may be in comments marked `USERCODE` \ No newline at end of file diff --git a/Socket_snippet/client.c b/Socket_snippet/client.c new file mode 100644 index 0000000..d50e5e5 --- /dev/null +++ b/Socket_snippet/client.c @@ -0,0 +1,120 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +// client-side functions +#include +#include +#include +#include + +#include "client.h" +#include "socket.h" + +/** + * @brief processData - process here some actions and make messages for server + * @param buf (o) - buffer for answer + * @param l - max length of buf + * @return amount of answer bytes + */ +static int process_data(char *buf, int l){ + if(!buf || !l) return 0; + /* USERCODE: get here some data to send */ + static double t0 = 0.; + if(dtime() - t0 > 1.){ + t0 = dtime(); + // send random commands each 1 second + int x = rand() % 600; + const char *cmd = NULL; + if(x < 100) cmd = "time"; + else if(x < 200) cmd = "getval1"; + else if(x < 300) cmd = "getval2"; + else if(x < 400) cmd = "ping"; + if(cmd) snprintf(buf, l, "%s", cmd); + else{ + if(x < 500) snprintf(buf, l, "setval1=%d", rand() & 0xff); + else snprintf(buf, l, "setval2=%d", rand() & 0xff); + } + return strlen(buf); + } + return 0; +} + +/** + * @brief process_server_message - parse messages from server and make an answer + * @param buf (io) - incoming message (the answer will be in this buffer) + * @param l - buff max length + * @return amount of answer bytes + */ +static int process_server_message(char *buf, int l){ + /* USERCODE inside this funtion */ + if(!buf || !l) return 0; + // just show on screen + green("SERVER send: %s\n", buf); + return 0; +} + + +/** + * check data from fd (polling function for client) + * @param fd - file descriptor + * @return 0 in case of timeout, 1 in case of fd have data, -1 if error + */ +static int canberead(int fd){ + fd_set fds; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100; + FD_ZERO(&fds); + FD_SET(fd, &fds); + do{ + int rc = select(fd+1, &fds, NULL, NULL, &timeout); + if(rc < 0){ + if(errno != EINTR){ + LOGWARN("select()"); + WARN("select()"); + return -1; + } + continue; + } + break; + }while(1); + if(FD_ISSET(fd, &fds)){ + //DBG("FD_ISSET"); + return 1; + } + return 0; +} + +void client(int sock){ + char buf[BUFLEN]; + while(1){ + int l = process_data(buf, BUFLEN-1); + if(l) sendmessage(sock, buf, l); + if(1 != canberead(sock)) continue; + int n = read(sock, buf, BUFLEN-1); + if(n == 0){ + WARNX("Server disconnected"); + signals(0); + } + buf[n] = 0; + DBG("Got from server: %s", buf); + LOGMSG("Got from server: %s", buf); + l = process_server_message(buf, n); + if(l) sendmessage(sock, buf, l); + } +} diff --git a/Socket_snippet/client.h b/Socket_snippet/client.h new file mode 100644 index 0000000..0ce68fc --- /dev/null +++ b/Socket_snippet/client.h @@ -0,0 +1,26 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#pragma once +#ifndef CLIENT_H__ +#define CLIENT_H__ + +// client-side functions +void client(int fd); + +#endif // CLIENT_H__ diff --git a/Socket_snippet/cmdlnopts.c b/Socket_snippet/cmdlnopts.c new file mode 100644 index 0000000..f106d49 --- /dev/null +++ b/Socket_snippet/cmdlnopts.c @@ -0,0 +1,83 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#include +#include +#include + +#include "cmdlnopts.h" +#include "usefull_macros.h" + +// default PID filename: +#define DEFAULT_PIDFILE "/tmp/usbsock.pid" + +static int help; +static glob_pars G = { + .pidfile = DEFAULT_PIDFILE, +}; +glob_pars *GP = &G; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +static myoption cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")}, + {"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, + {"client", NO_ARGS, NULL, 'c', arg_int, APTR(&G.client), _("run as client")}, + {"path", NEED_ARG, NULL, 'N', arg_string, APTR(&G.path), _("UNIX socket path/name (start from \\0 for no files)")}, + {"port", NEED_ARG, NULL, 'P', arg_string, APTR(&G.port), _("port to connect (for local TCP socket)")}, + {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), _("increase log verbose level (default: LOG_WARN) and messages (default: none)")}, + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +void parse_args(int argc, char **argv){ + int i; + size_t hlen = 1024; + char helpstring[1024], *hptr = helpstring; + snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n"); + // format of help: "Usage: progname [args]\n" + change_helpstring(helpstring); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + for(i = 0; i < argc; i++) + printf("Ignore parameter\t%s\n", argv[i]); + if(help) showhelp(-1, cmdlnopts); +} + +/** + * @brief verbose - print additional messages depending of G.verbose (add '\n' at end) + * @param levl - message level + * @param fmt - message + */ +void verbose(int levl, const char *fmt, ...){ + va_list ar; + if(levl > G.verbose) return; + va_start(ar, fmt); + vprintf(fmt, ar); + va_end(ar); + printf("\n"); +} diff --git a/Socket_snippet/cmdlnopts.h b/Socket_snippet/cmdlnopts.h new file mode 100644 index 0000000..da8945f --- /dev/null +++ b/Socket_snippet/cmdlnopts.h @@ -0,0 +1,40 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#pragma once +#ifndef CMDLNOPTS_H__ +#define CMDLNOPTS_H__ + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *pidfile; // name of PID file + char *logfile; // logging to this file + char *path; // path to UNIX-socket file + char *port; // local TCP socket port + int verbose; // verbose level: for messages & logging + int client; // ==1 if application runs in client mode +} glob_pars; + +extern glob_pars *GP; + +void parse_args(int argc, char **argv); +void verbose(int levl, const char *fmt, ...); + +#endif // CMDLNOPTS_H__ diff --git a/Socket_snippet/main.c b/Socket_snippet/main.c new file mode 100644 index 0000000..8f75fa8 --- /dev/null +++ b/Socket_snippet/main.c @@ -0,0 +1,108 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "socket.h" + +static pid_t childpid = 0; +static int isserver = 1; + +void signals(int sig){ + if(childpid){ // slave process + DBG("Child killed with sig=%d", sig); + LOGWARN("Child killed with sig=%d", sig); + exit(sig); + } + // master process + DBG("Master process"); + if(sig){ + DBG("Exit with signal %d", sig); + signal(sig, SIG_IGN); + LOGERR("Exit with signal %d", sig); + }else LOGERR("Exit"); + if(GP->pidfile && isserver){ + DBG("Unlink pid file"); + unlink(GP->pidfile); + } + exit(sig); +} + +int main(int argc, char **argv){ + char *self = strdup(argv[0]); + initial_setup(); + parse_args(argc, argv); + if(GP->logfile){ + int lvl = LOGLEVEL_WARN + GP->verbose; + DBG("level = %d", lvl); + if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY; + verbose(1, "Log file %s @ level %d\n", GP->logfile, lvl); + OPENLOG(GP->logfile, lvl, 1); + if(!globlog) WARNX("Can't create log file"); + } + if(GP->client) isserver = 0; + if(GP->port){ + if(GP->path){ + WARNX("Options `port` and `path` can't be used together! Point `port` for TCP socket or `path` for UNIX."); + return 1; + } + int port = atoi(GP->port); + if(port < PORTN_MIN || port > PORTN_MAX){ + WARNX("Wrong port value: %d", port); + return 1; + } + }else if(!GP->path) ERRX("You should point option `port` or `path`!"); + + if(isserver) check4running(self, GP->pidfile); + // signal reactions: + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z + LOGMSG("Started"); +#ifndef EBUG + if(isserver){ + unsigned int pause = 5; + while(1){ + childpid = fork(); + if(childpid){ // master + double t0 = dtime(); + LOGMSG("Created child with pid %d", childpid); + wait(NULL); + LOGWARN("Child %d died", childpid); + if(dtime() - t0 < 1.) pause += 5; + else pause = 1; + if(pause > 900) pause = 900; + sleep(pause); // wait a little before respawn + }else{ // slave + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + break; + } + } + } +#endif + if(GP->path) return start_socket(isserver, GP->path, FALSE); + if(GP->port) return start_socket(isserver, GP->port, TRUE); +} diff --git a/Socket_snippet/server.c b/Socket_snippet/server.c new file mode 100644 index 0000000..8f38bc0 --- /dev/null +++ b/Socket_snippet/server.c @@ -0,0 +1,150 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "server.h" +#include "socket.h" + +// server-side functions + +// command handlers +hresult pinghandler(int fd, _U_ const char *key, _U_ const char *val){ + sendstrmessage(fd, "PING"); + return RESULT_SILENCE; +} + +static int sendtime = 0; +hresult timehandler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + sendtime = 1; + return RESULT_OK; +} + +static int value1 = 0, value2 = 0; +hresult valsethandler(_U_ int fd, const char *key, const char *val){ + if(!val || !*val) return RESULT_BADVAL; + int i = atoi(val); + if(i < -65535 || i > 65535) return RESULT_BADVAL; + if(strcmp(key, "setval1") == 0) value1 = i; + else value2 = i; + return RESULT_OK; +} +hresult valgethandler(int fd, const char *key, _U_ const char *val){ + char buf[32]; + if(strcmp(key, "getval1") == 0) snprintf(buf, 32, "VAL1=%d", value1); + else snprintf(buf, 32, "VAL2=%d", value2); + sendstrmessage(fd, buf); + return RESULT_SILENCE; +} + +/* +hresult handler(_U_ int fd, _U_ const char *key, _U_ const char *val){ + ; +} +*/ + +static handleritem items[] = { + {pinghandler, "ping"}, + {timehandler, "time"}, + {valsethandler, "setval1"}, + {valsethandler, "setval2"}, + {valgethandler, "getval1"}, + {valgethandler, "getval2"}, + {NULL, NULL}, +}; + +/** + * @brief processData - process here some actions and make messages for all clients + * @param buf (o) - buffer for answer + * @param l - max length of buf + * @return amount of answer bytes + */ +static int process_data(char *buf, int l){ + if(!buf || !l) return 0; + /* USERCODE: get here some data to send all clients */ + if(sendtime){ + snprintf(buf, l, "TIME=%.2f", dtime()); + sendtime = 0; + return strlen(buf); + } + return 0; +} + +#define CLBUFSZ BUFSIZ + +void server(int sock){ + if(listen(sock, MAXCLIENTS) == -1){ + WARN("listen"); + LOGWARN("listen"); + return; + } + int nfd = 1; // only one socket @start + struct pollfd poll_set[MAXCLIENTS+1]; + char buffers[MAXCLIENTS][CLBUFSZ]; // buffers for data reading + bzero(poll_set, sizeof(poll_set)); + // ZERO - listening server socket + poll_set[0].fd = sock; + poll_set[0].events = POLLIN; + while(1){ + poll(poll_set, nfd, 1); // max timeout - 1ms + if(poll_set[0].revents & POLLIN){ // check main for accept() + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int client = accept(sock, (struct sockaddr*)&addr, &len); + DBG("New connection"); + LOGMSG("SERVER got connection, fd=%d", client); + if(nfd == MAXCLIENTS + 1){ + LOGWARN("Max amount of connections, disconnect fd=%d", client); + WARNX("Limit of connections reached"); + close(client); + }else{ + memset(&poll_set[nfd], 0, sizeof(struct pollfd)); + poll_set[nfd].fd = client; + poll_set[nfd].events = POLLIN; + ++nfd; + } + } + char buff[BUFLEN]; + int l = 0; + // process some data & send messages to ALL + if((l = process_data(buff, BUFLEN-1))){ + DBG("Send to %d clients", nfd - 1); + for(int i = 1; i < nfd; ++i) + sendmessage(poll_set[i].fd, buff, l); + } + // scan connections + for(int fdidx = 1; fdidx < nfd; ++fdidx){ + if((poll_set[fdidx].revents & POLLIN) == 0) continue; + int fd = poll_set[fdidx].fd; + if(!processData(fd, items, buffers[fdidx-1], CLBUFSZ)){ // socket closed + DBG("Client fd=%d disconnected", fd); + LOGMSG("SERVER client fd=%d disconnected", fd); + buffers[fdidx-1][0] = 0; // clear rest of data in buffer + close(fd); + // move last FD to current position + poll_set[fdidx] = poll_set[nfd - 1]; + --nfd; + } + } + } +} diff --git a/Socket_snippet/server.h b/Socket_snippet/server.h new file mode 100644 index 0000000..3cef966 --- /dev/null +++ b/Socket_snippet/server.h @@ -0,0 +1,26 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#pragma once +#ifndef SERVER_H__ +#define SERVER_H__ + +// server-side functions +void server(int fd); + +#endif // SERVER_H__ diff --git a/Socket_snippet/socket b/Socket_snippet/socket new file mode 100755 index 0000000..0d17902 Binary files /dev/null and b/Socket_snippet/socket differ diff --git a/Socket_snippet/socket.c b/Socket_snippet/socket.c new file mode 100644 index 0000000..508543f --- /dev/null +++ b/Socket_snippet/socket.c @@ -0,0 +1,213 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#include // isspace +#include +#include +#include +#include +#include // unix socket +#include + +#include "cmdlnopts.h" +#include "client.h" +#include "server.h" +#include "socket.h" + +/** + * @brief start_socket - create socket and run client or server + * @param isserver - TRUE for server, FALSE for client + * @param path - UNIX-socket path or local INET socket port + * @param isnet - TRUE for INET socket, FALSE for UNIX + * @return 0 if OK + */ +int start_socket(int isserver, char *path, int isnet){ + if(!path) return 1; + DBG("path/port: %s", path); + int sock = -1; + struct addrinfo hints = {0}, *res; + struct sockaddr_un unaddr = {0}; + if(isnet){ + DBG("Network socket"); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if(getaddrinfo("127.0.0.1", path, &hints, &res) != 0){ + ERR("getaddrinfo"); + } + }else{ + DBG("UNIX socket"); + char apath[128]; + if(*path == 0){ + DBG("convert name"); + apath[0] = 0; + strncpy(apath+1, path+1, 126); + }else if(strncmp("\\0", path, 2) == 0){ + DBG("convert name"); + apath[0] = 0; + strncpy(apath+1, path+2, 126); + }else strcpy(apath, path); + unaddr.sun_family = AF_UNIX; + hints.ai_addr = (struct sockaddr*) &unaddr; + hints.ai_addrlen = sizeof(unaddr); + memcpy(unaddr.sun_path, apath, 106); // if sun_path[0] == 0 we don't create a file + hints.ai_family = AF_UNIX; + hints.ai_socktype = SOCK_SEQPACKET; + res = &hints; + } + for(struct addrinfo *p = res; p; p = p->ai_next){ + if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0){ // or SOCK_STREAM? + LOGWARN("socket()"); + WARN("socket()"); + continue; + } + if(isserver){ + int reuseaddr = 1; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){ + LOGWARN("setsockopt()"); + WARN("setsockopt()"); + close(sock); sock = -1; + continue; + } + if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){ + LOGWARN("bind()"); + WARN("bind()"); + close(sock); sock = -1; + continue; + } + int enable = 1; + if(ioctl(sock, FIONBIO, (void *)&enable) < 0){ // make socket nonblocking + LOGERR("Can't make socket nonblocking"); + ERRX("ioctl()"); + } + }else{ + if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){ + LOGWARN("connect()"); + WARN("connect()"); + close(sock); sock = -1; + } + } + break; + } + if(sock < 0){ + LOGERR("Can't open socket"); + ERRX("Can't open socket"); + } + if(isnet) freeaddrinfo(res); + if(isserver) server(sock); + else client(sock); + close(sock); + signals(0); + return 0; +} + +// simple wrapper over write: add missed newline and log data +void sendmessage(int fd, const char *msg, int l){ + if(fd < 1 || !msg || l < 1) return; + DBG("send to fd %d: %s [%d]", fd, msg, l); + char *tmpbuf = MALLOC(char, l+1); + memcpy(tmpbuf, msg, l); + if(msg[l-1] != '\n') tmpbuf[l++] = '\n'; + if(l != write(fd, tmpbuf, l)){ + LOGWARN("write()"); + WARN("write()"); + }else{ + if(globlog){ // logging turned ON + tmpbuf[l-1] = 0; // remove trailing '\n' for logging + LOGMSG("SEND to fd %d: %s", fd, tmpbuf); + } + } + FREE(tmpbuf); +} +void sendstrmessage(int fd, const char *msg){ + if(fd < 1 || !msg) return; + int l = strlen(msg); + sendmessage(fd, msg, l); +} + +// text messages for `hresult` +static const char *resmessages[] = { + [RESULT_OK] = "OK", + [RESULT_FAIL] = "FAIL", + [RESULT_BADKEY] = "BADKEY", + [RESULT_BADVAL] = "BADVAL", + [RESULT_SILENCE] = "", +}; + +const char *hresult2str(hresult r){ + if(r >= RESULT_NUM) return "BADRESULT"; + return resmessages[r]; +} + +// parse string of data (command or key=val) +// the CONTENT of buffer `str` WILL BE BROKEN! +static void parsestring(int fd, handleritem *handlers, char *str){ + if(fd < 1 || !handlers || !handlers->key || !str || !*str) return; + DBG("Got string %s", str); + // remove starting spaces in key + while(isspace(*str)) ++str; + char *val = strchr(str, '='); + if(val){ // get value: remove starting spaces in val + *val++ = 0; + while(isspace(*val)) ++val; + } + // remove trailing spaces in key + char *e = str + strlen(str) - 1; // last key symbol + while(isspace(*e) && e > str) --e; + e[1] = 0; + // now we have key (`str`) and val (or NULL) + DBG("key=%s, val=%s", str, val); + for(handleritem *h = handlers; h->handler; ++h){ + if(strcmp(str, h->key) == 0){ // found command + if(h->handler){ + hresult r = h->handler(fd, str, val); + sendstrmessage(fd, hresult2str(r)); + }else sendstrmessage(fd, resmessages[RESULT_FAIL]); + return; + } + } + sendstrmessage(fd, resmessages[RESULT_BADKEY]); +} + +/** + * @brief processData - read (if available) data from fd and run processing, sending to fd messages for each command + * @param fd - socket file descriptor + * @param handlers - NULL-terminated array of handlers + * @param buf (io) - zero-terminated buffer for storing rest of data (without newline), its content will be changed + * @param buflen - its length + * @return FALSE if client closed (nothing to read) + */ +int processData(int fd, handleritem *handlers, char *buf, int buflen){ + int curlen = strlen(buf); + if(curlen == buflen-1) curlen = 0; // buffer overflow - clear old content + ssize_t rd = read(fd, buf + curlen, buflen-1 - curlen); + if(rd <= 0) return FALSE; + DBG("got %s[%zd] from %d", buf, rd, fd); + char *restofdata = buf, *eptr = buf + curlen + rd; + *eptr = 0; + do{ + char *nl = strchr(restofdata, '\n'); + if(!nl) break; + *nl++ = 0; + parsestring(fd, handlers, restofdata); + restofdata = nl; + DBG("rest of data: %s", restofdata); + }while(1); + if(restofdata != buf) memmove(buf, restofdata, eptr - restofdata + 1); + return TRUE; +} diff --git a/Socket_snippet/socket.h b/Socket_snippet/socket.h new file mode 100644 index 0000000..ecd6e3f --- /dev/null +++ b/Socket_snippet/socket.h @@ -0,0 +1,56 @@ +/* + * This file is part of the socksnippet project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 3 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, see . + */ + +#pragma once +#ifndef SERSOCK_H__ +#define SERSOCK_H__ + +// max & min TCP socket port number +#define PORTN_MAX (65535) +#define PORTN_MIN (1024) + +#define BUFLEN (1024) +// Max amount of connections +#define MAXCLIENTS (30) + +typedef enum{ + RESULT_OK, // all OK + RESULT_FAIL, // failed running command + RESULT_BADVAL, // bad key's value + RESULT_BADKEY, // bad key + RESULT_SILENCE, // send nothing to client + RESULT_NUM +} hresult; + +const char *hresult2str(hresult r); + +// fd - socket fd to send private messages, key, val - key and its value +typedef hresult (*mesghandler)(int fd, const char *key, const char *val); + +typedef struct{ + mesghandler handler; + const char *key; +} handleritem; + +int start_socket(int server, char *path, int isnet); +void sendmessage(int fd, const char *msg, int l); +void sendstrmessage(int fd, const char *msg); + +int processData(int fd, handleritem *handlers, char *buf, int buflen); + +#endif // SERSOCK_H__ diff --git a/Socket_snippet/socksnippet.cflags b/Socket_snippet/socksnippet.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/Socket_snippet/socksnippet.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/Socket_snippet/socksnippet.config b/Socket_snippet/socksnippet.config new file mode 100644 index 0000000..c49fee0 --- /dev/null +++ b/Socket_snippet/socksnippet.config @@ -0,0 +1,6 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 +#define EBUG +#define _GNU_SOURCE +#define _XOPEN_SOURCE=1111 + diff --git a/Socket_snippet/socksnippet.creator b/Socket_snippet/socksnippet.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/Socket_snippet/socksnippet.creator @@ -0,0 +1 @@ +[General] diff --git a/Socket_snippet/socksnippet.creator.user b/Socket_snippet/socksnippet.creator.user new file mode 100644 index 0000000..2810e57 --- /dev/null +++ b/Socket_snippet/socksnippet.creator.user @@ -0,0 +1,163 @@ + + + + + + EnvironmentId + {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + false + false + 1 + true + true + 0 + 8 + true + false + 2 + true + true + true + *.md, *.MD, Makefile + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + true + Builtin.DefaultTidyAndClazy + 4 + + + + true + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {91347f2c-5221-46a7-80b1-0a054ca02f79} + 0 + 0 + 0 + + /home/eddy/Docs/SAO/ELECTRONICS/CAN_controller/Socket_CANserver + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Default + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/Socket_snippet/socksnippet.cxxflags b/Socket_snippet/socksnippet.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/Socket_snippet/socksnippet.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/Socket_snippet/socksnippet.files b/Socket_snippet/socksnippet.files new file mode 100644 index 0000000..60feb38 --- /dev/null +++ b/Socket_snippet/socksnippet.files @@ -0,0 +1,9 @@ +client.c +client.h +cmdlnopts.c +cmdlnopts.h +main.c +server.c +server.h +socket.c +socket.h diff --git a/Socket_snippet/socksnippet.includes b/Socket_snippet/socksnippet.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/Socket_snippet/socksnippet.includes @@ -0,0 +1 @@ +. diff --git a/Socket_snippet/testcommands b/Socket_snippet/testcommands new file mode 100644 index 0000000..4adb552 --- /dev/null +++ b/Socket_snippet/testcommands @@ -0,0 +1,11 @@ + time + getval1 + setval1 = 56 + getval2 + setval2 = 189 + getval1 = asdf + getval2 = 000000asdffd fdassdf fda + time + a + getval1 +getval2