mirror of
https://github.com/eddyem/ARMsingleboard.git
synced 2026-01-31 20:35:11 +03:00
add simplest barrier management
This commit is contained in:
parent
7d16a4b1bd
commit
fdc3a6ec1c
79
schlagbaum/Makefile
Normal file
79
schlagbaum/Makefile
Normal file
@ -0,0 +1,79 @@
|
||||
# run `make DEF=...` to add extra defines
|
||||
CLIENT := sslclient
|
||||
SERVER := sslserver
|
||||
LDFLAGS += -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
|
||||
LDFLAGS += -lusefull_macros -lssl -lcrypto
|
||||
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
|
||||
SOBJDIR := mkserver
|
||||
COBJDIR := mkclient
|
||||
CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -pthread
|
||||
COMMSRCS := sslsock.c daemon.c cmdlnopts.c main.c
|
||||
SSRC := server.c $(COMMSRCS)
|
||||
CSRC := client.c $(COMMSRCS)
|
||||
SOBJS := $(addprefix $(SOBJDIR)/, $(SSRC:%.c=%.o))
|
||||
COBJS := $(addprefix $(COBJDIR)/, $(CSRC:%.c=%.o))
|
||||
SDEPS := $(SOBJS:.o=.d)
|
||||
CDEPS := $(COBJS:.o=.d)
|
||||
CC = gcc
|
||||
|
||||
TARGFILE := build.target
|
||||
|
||||
ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes)
|
||||
TARGET := $(file < $(TARGFILE))
|
||||
else
|
||||
TARGET := RELEASE
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET), DEBUG)
|
||||
.DEFAULT_GOAL := debug
|
||||
endif
|
||||
|
||||
release: CFLAGS += -flto
|
||||
release: LDFLAGS += -flto
|
||||
release: $(TARGFILE) $(CLIENT) $(SERVER)
|
||||
|
||||
debug: CFLAGS += -DEBUG -Werror
|
||||
debug: TARGET := DEBUG
|
||||
debug: $(TARGFILE) $(CLIENT) $(SERVER)
|
||||
|
||||
$(TARGFILE):
|
||||
@echo -e "\tTARGET: $(TARGET)\n"
|
||||
@echo "$(TARGET)" > $(TARGFILE)
|
||||
|
||||
$(CLIENT) : DEFINES += -DCLIENT
|
||||
$(CLIENT) : $(COBJDIR) $(COBJS)
|
||||
@echo -e "\tLD $(CLIENT)"
|
||||
$(CC) $(LDFLAGS) $(COBJS) -o $(CLIENT)
|
||||
|
||||
|
||||
$(SERVER) : DEFINES += -DSERVER
|
||||
$(SERVER) : $(SOBJDIR) $(SOBJS)
|
||||
@echo -e "\tLD $(SERVER)"
|
||||
$(CC) $(LDFLAGS) $(SOBJS) -o $(SERVER)
|
||||
|
||||
$(SOBJDIR):
|
||||
@mkdir $(SOBJDIR)
|
||||
|
||||
$(COBJDIR):
|
||||
@mkdir $(COBJDIR)
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
|
||||
$(COBJDIR)/%.o: %.c
|
||||
@echo -e "\tCC $<"
|
||||
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
|
||||
|
||||
$(SOBJDIR)/%.o: %.c
|
||||
@echo -e "\t\tCC $<"
|
||||
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
|
||||
|
||||
clean:
|
||||
@echo -e "\t\tCLEAN"
|
||||
@rm -rf $(SOBJDIR) $(COBJDIR) $(TARGFILE) 2>/dev/null || true
|
||||
|
||||
xclean: clean
|
||||
@rm -f $(PROGRAM)
|
||||
|
||||
.PHONY: clean xclean
|
||||
2
schlagbaum/Readme
Normal file
2
schlagbaum/Readme
Normal file
@ -0,0 +1,2 @@
|
||||
Barrier management using SSL-protected TCP-socket connection between client and server (check certs from both sides)
|
||||
|
||||
49
schlagbaum/ca/gen.sh
Executable file
49
schlagbaum/ca/gen.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
# https://gist.github.com/zapstar/4b51d7cfa74c7e709fcdaace19233443
|
||||
mkdir -p ca/private
|
||||
chmod 700 ca/private
|
||||
# NOTE: I'm using -nodes, this means that once anybody gets
|
||||
# their hands on this particular key, they can become this CA.
|
||||
openssl req \
|
||||
-x509 \
|
||||
-nodes \
|
||||
-days 36524 \
|
||||
-newkey rsa:4096 \
|
||||
-keyout ca/private/ca_key.pem \
|
||||
-out ca/ca_cert.pem \
|
||||
-subj "/C=RU/ST=KChR/L=Bukovo/O=SAO RAS/CN=sao.ru"
|
||||
|
||||
# Create server private key and certificate request
|
||||
mkdir -p server/private
|
||||
chmod 700 ca/private
|
||||
openssl genrsa -out server/private/server_key.pem 4096
|
||||
openssl req -new \
|
||||
-key server/private/server_key.pem \
|
||||
-out server/server.csr \
|
||||
-subj "/C=RU/ST=KChR/L=Bukovo/O=SAO RAS"
|
||||
|
||||
# Create client private key and certificate request
|
||||
mkdir -p client/private
|
||||
chmod 700 client/private
|
||||
openssl genrsa -out client/private/client_key.pem 4096
|
||||
openssl req -new \
|
||||
-key client/private/client_key.pem \
|
||||
-out client/client.csr \
|
||||
-subj "/C=RU/ST=KChR/L=Bukovo/O=SAO RAS"
|
||||
|
||||
# Generate certificates
|
||||
openssl x509 -req -days 36524 -in server/server.csr \
|
||||
-CA ca/ca_cert.pem -CAkey ca/private/ca_key.pem \
|
||||
-CAcreateserial -out server/server_cert.pem
|
||||
openssl x509 -req -days 36524 -in client/client.csr \
|
||||
-CA ca/ca_cert.pem -CAkey ca/private/ca_key.pem \
|
||||
-CAcreateserial -out client/client_cert.pem
|
||||
|
||||
# Now test both the server and the client
|
||||
# On one shell, run the following
|
||||
# openssl s_server -CAfile ca/ca_cert.pem -cert server/server_cert.pem -key server/private/server_key.pem -Verify 1
|
||||
# On another shell, run the following
|
||||
# openssl s_client -CAfile ca/ca_cert.pem -cert client/client_cert.pem -key client/private/client_key.pem
|
||||
# Once the negotiation is complete, any line you type is sent over to the other side.
|
||||
# By line, I mean some text followed by a keyboard return press.
|
||||
1
schlagbaum/ca_cert.pem
Symbolic link
1
schlagbaum/ca_cert.pem
Symbolic link
@ -0,0 +1 @@
|
||||
ca/ca/ca_cert.pem
|
||||
122
schlagbaum/client.c
Normal file
122
schlagbaum/client.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <usefull_macros.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "client.h"
|
||||
#include "cmdlnopts.h"
|
||||
#include "sslsock.h"
|
||||
#ifdef __arm__
|
||||
#include "gpio.h"
|
||||
|
||||
// GPIO25 - LEDopen, 8 - LEDclose
|
||||
static cmd_t client_in_gpios[] = {
|
||||
{18, CMD_OPEN},
|
||||
{23, CMD_CLOSE},
|
||||
{24, CMD_SIREN},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// LEDs
|
||||
static cmd_t client_out_gpios[] = {
|
||||
{10, CMD_LED0},
|
||||
{9, CMD_PING},
|
||||
{11, CMD_LED1},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static int SSL_nbread(SSL *ssl, char *buf, int bufsz){
|
||||
struct pollfd fds = {0};
|
||||
int fd = SSL_get_fd(ssl);
|
||||
fds.fd = fd;
|
||||
fds.events = POLLIN | POLLPRI;
|
||||
if(poll(&fds, 1, 1) < 0){ // wait no more than 1ms
|
||||
LOGWARN("SSL_nbread(): poll() failed");
|
||||
WARNX("poll()");
|
||||
return 0;
|
||||
}
|
||||
if(fds.revents & (POLLIN | POLLPRI)){
|
||||
//DBG("Got info in fd #%d", fd);
|
||||
int l = read_string(ssl, buf, bufsz);
|
||||
//DBG("read %d bytes", l);
|
||||
return l;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void readssl(SSL *ssl){
|
||||
char buf[BUFSIZ];
|
||||
int bytes = SSL_nbread(ssl, buf, BUFSIZ-1);
|
||||
//int bytes = read_string(ssl, buf, BUFSIZ-1);
|
||||
if(bytes > 0){
|
||||
buf[bytes] = 0;
|
||||
verbose(1, "Received: \"%s\"", buf);
|
||||
handle_message(buf, client_out_gpios);
|
||||
}else if(bytes < 0){
|
||||
LOGWARN("Server disconnected or other error");
|
||||
ERRX("Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
static void sendcommands(SSL *ssl){
|
||||
char buf[BUFSIZ];
|
||||
char **curdata = G.commands;
|
||||
if(!curdata) return;
|
||||
while(*curdata){
|
||||
verbose(1, "Send: \"%s\"", *curdata);
|
||||
int l = snprintf(buf, BUFSIZ-1, "%s\n", *curdata);
|
||||
if(SSL_write(ssl, buf, l) <= 0) WARNX("SSL write error");
|
||||
readssl(ssl);
|
||||
++curdata;
|
||||
}
|
||||
double t0 = dtime();
|
||||
while(dtime() - t0 < 2.) readssl(ssl);
|
||||
}
|
||||
|
||||
void clientproc(SSL_CTX *ctx, int fd){
|
||||
FNAME();
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ctx);
|
||||
SSL_set_fd(ssl, fd);
|
||||
int c = SSL_connect(ssl);
|
||||
if(c < 0){
|
||||
LOGERR("SSL_connect()");
|
||||
ERRX("SSL_connect() error: %d", SSL_get_error(ssl, c));
|
||||
}
|
||||
int enable = 1;
|
||||
if(ioctl(fd, FIONBIO, (void *)&enable) < 0){
|
||||
LOGERR("Can't make socket nonblocking");
|
||||
ERRX("ioctl()");
|
||||
}
|
||||
if(G.commands){
|
||||
sendcommands(ssl);
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
return;
|
||||
}
|
||||
while(1){
|
||||
#ifdef __arm__
|
||||
poll_gpio(&ssl, 1, client_in_gpios);
|
||||
#endif
|
||||
readssl(ssl);
|
||||
}
|
||||
SSL_free(ssl);
|
||||
}
|
||||
23
schlagbaum/client.h
Normal file
23
schlagbaum/client.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sslsock.h"
|
||||
|
||||
void clientproc(SSL_CTX *ctx, int fd);
|
||||
1
schlagbaum/client_cert.pem
Symbolic link
1
schlagbaum/client_cert.pem
Symbolic link
@ -0,0 +1 @@
|
||||
ca/client/client_cert.pem
|
||||
1
schlagbaum/client_key.pem
Symbolic link
1
schlagbaum/client_key.pem
Symbolic link
@ -0,0 +1 @@
|
||||
ca/client/private/client_key.pem
|
||||
115
schlagbaum/cmdlnopts.c
Normal file
115
schlagbaum/cmdlnopts.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
|
||||
|
||||
/*
|
||||
* here are global parameters initialisation
|
||||
*/
|
||||
static int help;
|
||||
|
||||
#ifdef SERVER
|
||||
#define DEFCERT "server_cert.pem"
|
||||
#define DEFKEY "server_key.pem"
|
||||
#else
|
||||
#define DEFCERT "client_cert.pem"
|
||||
#define DEFKEY "client_key.pem"
|
||||
#endif
|
||||
#define DEFCA "ca_cert.pem"
|
||||
|
||||
#define DEFGPIO "/dev/gpiochip0"
|
||||
// default global parameters
|
||||
glob_pars G = {
|
||||
.pidfile = DEFAULT_PIDFILE,
|
||||
.port = DEFAULT_PORT,
|
||||
.cert = DEFCERT,
|
||||
.key = DEFKEY,
|
||||
.ca = DEFCA,
|
||||
#ifdef __arm__
|
||||
.gpiodevpath = DEFGPIO,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
static myoption cmdlnopts[] = {
|
||||
// common options
|
||||
{"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")},
|
||||
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")},
|
||||
{"certificate",NEED_ARG,NULL, 'c', arg_string, APTR(&G.cert), _("path to SSL sertificate (default: " DEFCERT ")")},
|
||||
{"key", NEED_ARG, NULL, 'k', arg_string, APTR(&G.key), _("path to SSL key (default: " DEFKEY ")")},
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("port to open (default: " DEFAULT_PORT ")")},
|
||||
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), _("increase log verbose level (default: LOG_WARN)")},
|
||||
{"ca", NEED_ARG, NULL, 'a', arg_string, APTR(&G.ca), _("path to SSL ca - base cert (default:" DEFCA ")")},
|
||||
#ifdef __arm__
|
||||
{"gpiopath",NEED_ARG, NULL, 'g', arg_string, APTR(&G.gpiodevpath),_("path to GPIO device (default:" DEFGPIO ")")},
|
||||
#endif
|
||||
#ifdef CLIENT
|
||||
{"server", NEED_ARG, NULL, 's', arg_string, APTR(&G.serverhost), _("server IP address or name")},
|
||||
{"command", MULT_PAR, NULL, 'C', arg_string, APTR(&G.commands), _("don't run client as daemon, just send given commands to server")},
|
||||
#endif
|
||||
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
|
||||
*/
|
||||
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);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
red("Ignored options:\n");
|
||||
for (i = 0; i < argc; i++)
|
||||
printf("\t%s\n", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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");
|
||||
}
|
||||
52
schlagbaum/cmdlnopts.h
Normal file
52
schlagbaum/cmdlnopts.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// default PID filename:
|
||||
#ifndef DEFAULT_PIDFILE
|
||||
#define DEFAULT_PIDFILE "/tmp/sslsock.pid"
|
||||
#endif
|
||||
#ifndef DEFAULT_PORT
|
||||
#define DEFAULT_PORT "4444"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* here are some typedef's for global data
|
||||
*/
|
||||
typedef struct{
|
||||
char *pidfile; // name of PID file
|
||||
char *logfile; // logging to this file
|
||||
char *cert; // sertificate
|
||||
char *key; // key
|
||||
char *port; // port number
|
||||
int verbose; // logfile verbose level
|
||||
char *ca; // ca
|
||||
#ifdef CLIENT
|
||||
char *serverhost; // server IP address
|
||||
char **commands; // don't run as daemon, just send given commands to server
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
char *gpiodevpath; // path to gpio device file
|
||||
#endif
|
||||
} glob_pars;
|
||||
|
||||
extern glob_pars G;
|
||||
|
||||
void parse_args(int argc, char **argv);
|
||||
void verbose(int levl, const char *fmt, ...);
|
||||
106
schlagbaum/daemon.c
Normal file
106
schlagbaum/daemon.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/prctl.h> // prctl
|
||||
#include <sys/wait.h> // wait
|
||||
#include <unistd.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
#include "daemon.h"
|
||||
#include "sslsock.h"
|
||||
|
||||
static pid_t childpid = -1;
|
||||
|
||||
void signals(int sig){
|
||||
if(childpid == 0){
|
||||
LOGWARN("Child killed with sig=%d", sig);
|
||||
exit(sig); // slave process
|
||||
}
|
||||
// master process
|
||||
if(sig){
|
||||
signal(sig, SIG_IGN);
|
||||
LOGERR("Exit with signal %d", sig);
|
||||
}else LOGERR("Exit");
|
||||
if(G.pidfile) unlink(G.pidfile);
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief start_daemon - daemonize
|
||||
* @param self - self name of process
|
||||
* @return error code or 0
|
||||
*/
|
||||
int start_daemon(_U_ char *self){
|
||||
// check args
|
||||
int port = atoi(G.port);
|
||||
if(port < 1024 || port > 65535){
|
||||
LOGERR("Wrong port value: %d", port);
|
||||
return 1;
|
||||
}
|
||||
FILE *f = fopen(G.cert, "r");
|
||||
if(!f) ERR("Can't open certificate file %s", G.cert);
|
||||
fclose(f);
|
||||
f = fopen(G.key, "r");
|
||||
if(!f) ERR("Can't open certificate key file %s", G.key);
|
||||
fclose(f);
|
||||
#ifdef EBUG
|
||||
printf("cert: %s, key: %s\n", G.cert, G.key);
|
||||
#endif
|
||||
#ifdef CLIENT
|
||||
//DBG("server: %s", G.serverhost);
|
||||
if(!G.serverhost) ERRX("Point server name");
|
||||
#endif
|
||||
if(G.logfile){
|
||||
int lvl = LOGLEVEL_WARN + G.verbose;
|
||||
DBG("level = %d", lvl);
|
||||
if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY;
|
||||
green("Log file %s @ level %d\n", G.logfile, lvl);
|
||||
OPENLOG(G.logfile, lvl, 1);
|
||||
}
|
||||
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
|
||||
#ifdef SERVER
|
||||
check4running(self, G.pidfile);
|
||||
#endif
|
||||
LOGMSG("Started");
|
||||
#ifndef EBUG
|
||||
#ifdef CLIENT
|
||||
if(G.commands) return open_socket();
|
||||
#endif
|
||||
while(1){
|
||||
childpid = fork();
|
||||
if(childpid){ // master
|
||||
LOGMSG("Created child with pid %d", childpid);
|
||||
wait(NULL);
|
||||
LOGWARN("Child %d died", childpid);
|
||||
sleep(1); // wait a little before respawn
|
||||
}else{ // slave
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// parent should never reach this part of code
|
||||
return open_socket();
|
||||
}
|
||||
22
schlagbaum/daemon.h
Normal file
22
schlagbaum/daemon.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int start_daemon(char *self);
|
||||
|
||||
222
schlagbaum/gpio.c
Normal file
222
schlagbaum/gpio.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
#include "gpio.h"
|
||||
|
||||
static int gpiofd = -1;
|
||||
static struct gpio_v2_line_request rq_in, rq_out;
|
||||
|
||||
// inputs and outputs
|
||||
static const int gpio_inputs[GPIO_IN_NUMBER] = {18, 23, 24, 25, 8, 7};
|
||||
static const int gpio_outputs[GPIO_OUT_NUMBER] = {17, 27, 22, 10, 9, 11};
|
||||
|
||||
// last time GPIO was activated
|
||||
static double gpio_set_time[GPIO_OUT_NUMBER] = {-1., -1., -1., -1., -1., -1.};
|
||||
|
||||
/**
|
||||
* @brief gpio_chkclr - clear outputs by timeout
|
||||
*/
|
||||
static void gpio_chkclr(){
|
||||
double tnow = dtime();
|
||||
for(int i = 0; i < GPIO_OUT_NUMBER; ++i){
|
||||
if(t[i] < 0.) continue;
|
||||
if(tnow - t[i] < GPIO_TIMEOUT) continue;
|
||||
gpio_clear_output(gpio_outputs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gpio_open_device - open GPIO device
|
||||
* @param path - path to device
|
||||
* @return device fd or -1 if error
|
||||
*/
|
||||
int gpio_open_device(const char *path){
|
||||
FNAME();
|
||||
gpiofd = open(path, O_RDONLY);
|
||||
if(gpiofd < 0){
|
||||
LOGERR("Unabled to open %s: %s", path, strerror(errno));
|
||||
WARNX("Can't open GPIO device %s", path);
|
||||
return -1;
|
||||
}
|
||||
struct gpiochip_info info;
|
||||
|
||||
// Query GPIO chip information
|
||||
if(-1 == ioctl(gpiofd, GPIO_GET_CHIPINFO_IOCTL, &info)){
|
||||
LOGERR("Unable to get chip info from ioctl: %s", strerror(errno));
|
||||
WARNX("Unable to get chip info");
|
||||
close(gpiofd);
|
||||
return -1;
|
||||
}
|
||||
verbose(2, "Chip name: %s", info.name);
|
||||
verbose(2, "Chip label: %s", info.label);
|
||||
verbose(2, "Number of lines: %d", info.lines);
|
||||
rq_in.fd = -1;
|
||||
rq_out.fd = -1;
|
||||
return gpiofd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gpio_set_outputs - set output pins (ACTIVE_LOW!!! so we need to invert incoming data for proper work)
|
||||
* @return rq.fd or -1 if failed
|
||||
*/
|
||||
int gpio_setup_outputs(){
|
||||
FNAME();
|
||||
bzero(&rq_out, sizeof(rq_out));
|
||||
for(int i = 0; i < GPIO_OUT_NUMBER; ++i)
|
||||
rq_out.offsets[i] = gpio_outputs[i];
|
||||
snprintf(rq_out.consumer, GPIO_MAX_NAME_SIZE-1, "outputs");
|
||||
rq_out.num_lines = GPIO_OUT_NUMBER;
|
||||
rq_out.config.flags = GPIO_V2_LINE_FLAG_OUTPUT | GPIO_V2_LINE_FLAG_OPEN_DRAIN | GPIO_V2_LINE_FLAG_ACTIVE_LOW | GPIO_V2_LINE_FLAG_BIAS_DISABLED;
|
||||
rq_out.config.num_attrs = 0;
|
||||
if(-1 == ioctl(gpiofd, GPIO_V2_GET_LINE_IOCTL, &rq_out)){
|
||||
LOGERR("Unable setup outputs: %s", strerror(errno));
|
||||
WARNX("Can't setup outputs");
|
||||
return -1;
|
||||
}
|
||||
return rq_out.fd;
|
||||
}
|
||||
|
||||
static int gpio_setreset(int input, int set){
|
||||
int idx = -1;
|
||||
for(int i = 0; i < GPIO_IN_NUMBER; ++i){
|
||||
if(gpio_inputs[i] == input){
|
||||
idx = i; break;
|
||||
}
|
||||
}
|
||||
DBG("idx = %d", idx);
|
||||
if(idx < 0 || idx > GPIO_OUT_NUMBER) return FALSE;
|
||||
struct gpio_v2_line_values values;
|
||||
bzero(&values, sizeof(values));
|
||||
uint64_t val = (1<<idx) & GPIO_OUT_MASK;
|
||||
values.mask = val;
|
||||
values.bits = set ? 0 : val; // invert bit due to GPIO_V2_LINE_FLAG_ACTIVE_LOW
|
||||
DBG("mask=%" PRIu64 ", val=%" PRIu64, values.mask, values.bits);
|
||||
if(-1 == ioctl(rq_out.fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values)){
|
||||
LOGERR("Unable to change GPIO values (mask=%" PRIu64 ", val=%" PRIu64 ": %s", values.mask, values.bits, strerror(errno));
|
||||
WARNX("Can't change GPIO values");
|
||||
return FALSE;
|
||||
}
|
||||
// change last event time
|
||||
if(set) gpio_set_time[idx] = 0;
|
||||
else gpio_set_time[idx] = dtime();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gpio_set_output - set to 1 out pin according to input number
|
||||
* @param input - number of input pin
|
||||
* @return true if all OK, false if failed
|
||||
*/
|
||||
int gpio_set_output(int input){
|
||||
return gpio_setreset(input, 1);
|
||||
}
|
||||
/**
|
||||
* @brief gpio_clear_output - clear to 0 output pin
|
||||
* @param input - number of input pin
|
||||
* @return true if all OK, false if failed
|
||||
*/
|
||||
int gpio_clear_output(int input){
|
||||
return gpio_setreset(input, 0);
|
||||
}
|
||||
|
||||
|
||||
int gpio_setup_inputs(){
|
||||
FNAME();
|
||||
bzero(&rq_in, sizeof(rq_in));
|
||||
for(int i = 0; i < GPIO_IN_NUMBER; ++i)
|
||||
rq_in.offsets[i] = gpio_inputs[i];
|
||||
snprintf(rq_in.consumer, GPIO_MAX_NAME_SIZE-1, "inputs");
|
||||
rq_in.num_lines = GPIO_IN_NUMBER;
|
||||
rq_in.config.flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_BIAS_PULL_UP | GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING;
|
||||
rq_in.config.num_attrs = 0;
|
||||
if(-1 == ioctl(gpiofd, GPIO_V2_GET_LINE_IOCTL, &rq_in)){
|
||||
LOGERR("Unable to setup inputs: %s", strerror(errno));
|
||||
WARNX("Can't setup inputs");
|
||||
return -1;
|
||||
}
|
||||
return rq_in.fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gpio_poll - poll inputs, return only last event
|
||||
* @return bit mask of changing inputs (edge falling), 0 if nothing happen or -1 if error
|
||||
*/
|
||||
int gpio_poll(uint32_t *up, uint32_t *down){
|
||||
struct pollfd pfd;
|
||||
struct gpio_v2_line_event event;
|
||||
bzero(&pfd, sizeof(pfd));
|
||||
bzero(&event, sizeof(event));
|
||||
if(up) *up = 0;
|
||||
if(down) *down = 0;
|
||||
gpio_chkclr(); // clear old outputs
|
||||
do{
|
||||
pfd.fd = rq_in.fd;
|
||||
pfd.events = POLLIN | POLLPRI;
|
||||
int p = poll(&pfd, 1, 1);
|
||||
if(p == 0) break; // nothing happened
|
||||
else if(p == -1){
|
||||
LOGERR("poll() error: %s", strerror(errno));
|
||||
WARNX("GPIO poll() error");
|
||||
return -1;
|
||||
}
|
||||
DBG("Got GPIO event!");
|
||||
int r = read(rq_in.fd, &event, sizeof(struct gpio_v2_line_event));
|
||||
if(r != sizeof(struct gpio_v2_line_event)){
|
||||
LOGERR("Error reading GPIO data");
|
||||
WARNX("Error reading GPIO data");
|
||||
return -1;
|
||||
}
|
||||
verbose(1, "Got event:\n\ttimestamp=%" PRIu64 "\n\tid=%d\n\toff=%d\n\tseqno=%d\n\tlineseqno=%d",
|
||||
event.timestamp_ns, event.id, event.offset, event.seqno, event.line_seqno);
|
||||
if(up){
|
||||
if(event.id == GPIO_V2_LINE_EVENT_RISING_EDGE){
|
||||
*up = event.offset;
|
||||
break;
|
||||
}else *up = 0;
|
||||
}
|
||||
if(down){
|
||||
if(event.id == GPIO_V2_LINE_EVENT_FALLING_EDGE){
|
||||
*down = event.offset;
|
||||
break;
|
||||
}else *down = 0;
|
||||
}
|
||||
}while(1);
|
||||
return (int)event.offset;
|
||||
}
|
||||
|
||||
void gpio_close(){
|
||||
if(gpiofd > -1){
|
||||
close(gpiofd);
|
||||
if(rq_in.fd > -1) close(rq_in.fd);
|
||||
if(rq_out.fd > -1) close(rq_out.fd);
|
||||
}
|
||||
}
|
||||
|
||||
48
schlagbaum/gpio.h
Normal file
48
schlagbaum/gpio.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// GPIO polling interval - 50ms
|
||||
#define GPIO_POLL_INTERVAL (0.05)
|
||||
|
||||
// amount of in/out GPIO pins
|
||||
#define GPIO_IN_NUMBER (6)
|
||||
#define GPIO_OUT_NUMBER (6)
|
||||
|
||||
// maximal GPIO number
|
||||
#define GPIO_MAX_NUMBER (32)
|
||||
|
||||
// 6 outputs
|
||||
#define GPIO_OUT_MASK 0x3f
|
||||
// 6 inputs
|
||||
#define GPIO_IN_MASK 0x3f
|
||||
|
||||
// timeout - clear GPIO after receiving command - 1 minute
|
||||
#define GPIO_TIMEOUT (60.)
|
||||
|
||||
int gpio_open_device(const char *path);
|
||||
int gpio_setup_outputs();
|
||||
int gpio_setup_inputs();
|
||||
int gpio_poll(uint32_t *up, uint32_t *down);
|
||||
int gpio_set_output(int input);
|
||||
int gpio_clear_output(int input);
|
||||
void gpio_close();
|
||||
|
||||
30
schlagbaum/main.c
Normal file
30
schlagbaum/main.c
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "cmdlnopts.h"
|
||||
|
||||
int main(int argc, char **argv){
|
||||
char *self = argv[0];
|
||||
initial_setup();
|
||||
parse_args(argc, argv);
|
||||
return start_daemon(self);
|
||||
}
|
||||
1
schlagbaum/schlagbaum.cflags
Normal file
1
schlagbaum/schlagbaum.cflags
Normal file
@ -0,0 +1 @@
|
||||
-std=c17
|
||||
7
schlagbaum/schlagbaum.config
Normal file
7
schlagbaum/schlagbaum.config
Normal file
@ -0,0 +1,7 @@
|
||||
#define _GNU_SOURCE
|
||||
#define _XOPEN_SOURCE 1111
|
||||
#define CLIENT
|
||||
#define EBUG
|
||||
|
||||
//#define SERVER
|
||||
|
||||
1
schlagbaum/schlagbaum.creator
Normal file
1
schlagbaum/schlagbaum.creator
Normal file
@ -0,0 +1 @@
|
||||
[General]
|
||||
181
schlagbaum/schlagbaum.creator.user
Normal file
181
schlagbaum/schlagbaum.creator.user
Normal file
@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 13.0.1, 2024-06-05T12:39:52. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="qlonglong">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||
<valuemap type="QVariantMap" key="ClangTools">
|
||||
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">8</value>
|
||||
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Big/Data/C_sources/shlagbaum</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
1
schlagbaum/schlagbaum.cxxflags
Normal file
1
schlagbaum/schlagbaum.cxxflags
Normal file
@ -0,0 +1 @@
|
||||
-std=c++17
|
||||
15
schlagbaum/schlagbaum.files
Normal file
15
schlagbaum/schlagbaum.files
Normal file
@ -0,0 +1,15 @@
|
||||
client.c
|
||||
client.h
|
||||
cmdlnopts.c
|
||||
cmdlnopts.h
|
||||
daemon.c
|
||||
daemon.h
|
||||
gpio.c
|
||||
gpio.h
|
||||
main.c
|
||||
server.c
|
||||
server.h
|
||||
schlagbaum.c
|
||||
schlagbaum.h
|
||||
sslsock.c
|
||||
sslsock.h
|
||||
1
schlagbaum/schlagbaum.includes
Normal file
1
schlagbaum/schlagbaum.includes
Normal file
@ -0,0 +1 @@
|
||||
.
|
||||
173
schlagbaum/server.c
Normal file
173
schlagbaum/server.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <usefull_macros.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "server.h"
|
||||
#ifdef __arm__
|
||||
#include "gpio.h"
|
||||
|
||||
// GPIO25 - LEDopen, 8 - LEDclose
|
||||
static cmd_t server_in_gpios[] = {
|
||||
{25, CMD_LED0},
|
||||
{8, CMD_LED1},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// 17 - open, 27 - close, 22 - siren
|
||||
static cmd_t server_out_gpios[] = {
|
||||
{17, CMD_OPEN},
|
||||
{27, CMD_CLOSE},
|
||||
{22, CMD_SIREN},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static const char *maxcl = "Max client number reached, connect later\n";
|
||||
static const char *sslerr = "SSL error occured\n";
|
||||
|
||||
|
||||
|
||||
// return 0 if client disconnected
|
||||
static int handle_connection(SSL *ssl){
|
||||
char buf[1024];
|
||||
int r = read_string(ssl, buf, 1024);
|
||||
if(r < 0) return 0;
|
||||
int sd = SSL_get_fd(ssl);
|
||||
int l = 0;
|
||||
printf("Client %d msg: \"%s\"\n", sd, buf);
|
||||
LOGDBG("fd=%d, message=%s", sd, buf);
|
||||
const char *ans = "FAIL";
|
||||
#ifdef __arm__
|
||||
if(strcmp(buf, CMD_OPEN) || strcmp(buf, CMD_CLOSE)){ // shut both 17/27 channels before run cmd
|
||||
gpio_clear_output(17);
|
||||
gpio_clear_output(27);
|
||||
usleep(100000); // wait a little
|
||||
}
|
||||
#endif
|
||||
if(handle_message(buf, server_out_gpios)) ans = "OK";
|
||||
l = snprintf(buf, 1023, "%s\n", ans);
|
||||
if(SSL_write(ssl, buf, l) <= 0) WARNX("SSL write error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief timeouted_sslaccept - SSL_accept with timeout
|
||||
* @param ssl - SSL
|
||||
* @return 1 if connection ready or 0 if error
|
||||
*/
|
||||
static int timeouted_sslaccept(SSL *ssl){
|
||||
double t0 = dtime();
|
||||
while(dtime() - t0 < ACCEPT_TIMEOUT){
|
||||
int x = SSL_accept(ssl);
|
||||
if(x < 0){
|
||||
int sslerr = SSL_get_error(ssl, x);
|
||||
if(SSL_ERROR_WANT_READ == sslerr ||
|
||||
SSL_ERROR_WANT_WRITE == sslerr) continue;
|
||||
DBG("SSL error %d", sslerr);
|
||||
return FALSE;
|
||||
}
|
||||
else return TRUE;
|
||||
}
|
||||
DBG("Timeout");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void serverproc(SSL_CTX *ctx, int fd){
|
||||
int enable = 1;
|
||||
if(ioctl(fd, FIONBIO, (void *)&enable) < 0){
|
||||
LOGERR("Can't make socket nonblocking");
|
||||
ERRX("ioctl()");
|
||||
}
|
||||
int nfd = 1; // only one listening socket @start
|
||||
struct pollfd poll_set[BACKLOG+1];
|
||||
memset(poll_set, 0, sizeof(poll_set));
|
||||
poll_set[0].fd = fd;
|
||||
poll_set[0].events = POLLIN | POLLPRI;
|
||||
SSL *ssls[BACKLOG+1] = {0}; // !!! start from 1 - like in poll_set !!!
|
||||
double t0 = dtime();
|
||||
while(1){
|
||||
double t = dtime();
|
||||
if(t - t0 > PING_TIMEOUT){
|
||||
t0 = t;
|
||||
char buf[32];
|
||||
int l = sprintf(buf, "%s\n", CMD_PING);
|
||||
for(int i = nfd-1; i > 0; --i){
|
||||
DBG("send test to fd[%d]=%d", i, poll_set[i].fd);
|
||||
if(SSL_write(ssls[i], buf, l) <= 0) WARNX("SSL write error");
|
||||
}
|
||||
}
|
||||
#ifdef __arm__
|
||||
poll_gpio(ssls, nfd, server_in_gpios);
|
||||
#endif
|
||||
poll(poll_set, nfd, 1); // max timeout - 1ms
|
||||
// check for accept()
|
||||
if(poll_set[0].revents & (POLLIN | POLLPRI)){
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
int client = accept4(fd, (struct sockaddr*)&addr, &len, SOCK_NONBLOCK); // non-blocking for timeout of SSL_accept
|
||||
DBG("Connection: %s @ %d (fd=%d)\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), client);
|
||||
LOGMSG("Client %s connected to port %d (fd=%d)", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), client);
|
||||
if(nfd == BACKLOG + 1){
|
||||
LOGWARN("Max amount of connections: disconnect fd=%d", client);
|
||||
WARNX("Limit of connections reached");
|
||||
send(client, maxcl, sizeof(maxcl)-1, MSG_NOSIGNAL);
|
||||
close(client);
|
||||
}else{
|
||||
DBG("New ssl");
|
||||
SSL *ssl = SSL_new(ctx);
|
||||
SSL_set_fd(ssl, client);
|
||||
DBG("Accept");
|
||||
if(timeouted_sslaccept(ssl)){
|
||||
DBG("OK");
|
||||
ssls[nfd] = ssl;
|
||||
bzero(&poll_set[nfd], sizeof(struct pollfd));
|
||||
poll_set[nfd].fd = client;
|
||||
poll_set[nfd].events = POLLIN | POLLPRI;
|
||||
DBG("nfd=%d, fd=%d, events=0x%x", nfd, poll_set[nfd].fd, poll_set[nfd].events);
|
||||
++nfd;
|
||||
}else{
|
||||
LOGERR("SSL_accept()");
|
||||
WARNX("SSL_accept()");
|
||||
SSL_free(ssl);
|
||||
send(client, sslerr, sizeof(sslerr)-1, MSG_NOSIGNAL);
|
||||
close(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
// scan connections
|
||||
for(int fdidx = 1; fdidx < nfd; ++fdidx){
|
||||
if(poll_set[fdidx].revents) DBG("%d, revents=0x%x", fdidx, poll_set[fdidx].revents);
|
||||
if((poll_set[fdidx].revents & (POLLIN | POLLPRI)) == 0) continue;
|
||||
DBG("%d poll", fdidx);
|
||||
int fd = poll_set[fdidx].fd;
|
||||
if(!handle_connection(ssls[fdidx])){ // socket closed
|
||||
SSL_free(ssls[fdidx]);
|
||||
DBG("Client fd=%d disconnected", fd);
|
||||
LOGMSG("Client fd=%d disconnected", fd);
|
||||
close(fd);
|
||||
if(--nfd > fdidx){ // move last FD to current position
|
||||
poll_set[fdidx] = poll_set[nfd];
|
||||
ssls[fdidx] = ssls[nfd];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
schlagbaum/server.h
Normal file
26
schlagbaum/server.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sslsock.h"
|
||||
|
||||
// timeout of SSL_accept (seconds)
|
||||
#define ACCEPT_TIMEOUT (10.)
|
||||
|
||||
void serverproc(SSL_CTX *ctx, int fd);
|
||||
1
schlagbaum/server_cert.pem
Symbolic link
1
schlagbaum/server_cert.pem
Symbolic link
@ -0,0 +1 @@
|
||||
ca/server/server_cert.pem
|
||||
1
schlagbaum/server_key.pem
Symbolic link
1
schlagbaum/server_key.pem
Symbolic link
@ -0,0 +1 @@
|
||||
ca/server/private/server_key.pem
|
||||
252
schlagbaum/sslsock.c
Normal file
252
schlagbaum/sslsock.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <resolv.h>
|
||||
#include <signal.h> // pthread_kill
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
#include "sslsock.h"
|
||||
#ifdef SERVER
|
||||
#include "server.h"
|
||||
#else
|
||||
#include "client.h"
|
||||
#endif
|
||||
|
||||
#ifdef SERVER
|
||||
static int OpenConn(int port){
|
||||
int sd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if(sd < 0){
|
||||
LOGERR("Can't open socket");
|
||||
ERRX("socket()");
|
||||
}
|
||||
int enable = 1;
|
||||
// allow reuse of descriptor
|
||||
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&enable, sizeof(int)) < 0){
|
||||
LOGERR("Can't apply SO_REUSEADDR to socket");
|
||||
ERRX("setsockopt()");
|
||||
}
|
||||
struct sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
if(bind(sd, (struct sockaddr*)&addr, sizeof(addr))){
|
||||
LOGWARN("Can't bind port %d", port);
|
||||
ERRX("bind()");
|
||||
}
|
||||
if(listen(sd, BACKLOG)){
|
||||
LOGWARN("Can't listen()");
|
||||
ERRX("listen()");
|
||||
}
|
||||
return sd;
|
||||
}
|
||||
#else
|
||||
static int OpenConn(int port){
|
||||
FNAME();
|
||||
int sd;
|
||||
struct hostent *host;
|
||||
struct sockaddr_in addr;
|
||||
if((host = gethostbyname(G.serverhost)) == NULL ){
|
||||
LOGWARN("gethostbyname(%s) error", G.serverhost);
|
||||
ERRX("gethostbyname()");
|
||||
}
|
||||
sd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
DBG("sd=%d", sd);
|
||||
bzero(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = *(long*)(host->h_addr);
|
||||
if(connect(sd, (struct sockaddr*)&addr, sizeof(addr))){
|
||||
close(sd);
|
||||
LOGWARN("Can't connect to %s", G.serverhost);
|
||||
ERRX("Can't connect to %s", G.serverhost);
|
||||
}
|
||||
return sd;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SSL_CTX* InitCTX(void){
|
||||
const SSL_METHOD *method;
|
||||
SSL_CTX *ctx;
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
method =
|
||||
#ifdef CLIENT
|
||||
TLS_client_method();
|
||||
#else
|
||||
TLS_server_method();
|
||||
#endif
|
||||
ctx = SSL_CTX_new(method);
|
||||
if(!ctx){
|
||||
LOGWARN("Can't create SSL context");
|
||||
ERRX("SSL_CTX_new()");
|
||||
}
|
||||
if(SSL_CTX_load_verify_locations(ctx, G.ca, NULL) != 1){
|
||||
LOGWARN("Could not set the CA file location\n");
|
||||
ERRX("Could not set the CA file location\n");
|
||||
}
|
||||
#ifdef SERVER
|
||||
|
||||
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(G.ca));
|
||||
#endif
|
||||
if(SSL_CTX_use_certificate_file(ctx, G.cert, SSL_FILETYPE_PEM) <= 0){
|
||||
LOGWARN("Can't use SSL certificate %s", G.cert);
|
||||
ERRX("Can't use SSL certificate %s", G.cert);
|
||||
}
|
||||
if(SSL_CTX_use_PrivateKey_file(ctx, G.key, SSL_FILETYPE_PEM) <= 0){
|
||||
LOGWARN("Can't use SSL key %s", G.key);
|
||||
ERRX("Can't use SSL key %s", G.key);
|
||||
}
|
||||
if(!SSL_CTX_check_private_key(ctx)){
|
||||
LOGWARN("Private key does not match the public certificate\n");
|
||||
ERRX("Private key does not match the public certificate\n");
|
||||
}
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
||||
#ifdef SERVER
|
||||
SSL_CTX_set_verify(ctx, // Specify that we need to verify the client as well
|
||||
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||
NULL);
|
||||
#else
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
|
||||
#endif
|
||||
SSL_CTX_set_verify_depth(ctx, 1); // We accept only certificates signed only by the CA himself
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int open_socket(){
|
||||
int fd;
|
||||
#ifdef __arm__
|
||||
#ifndef SERVER
|
||||
if(!G.commands){ // open devices if not client
|
||||
#endif
|
||||
if(-1 == gpio_open_device(G.gpiodevpath)) ERRX("Can't open GPIO device");
|
||||
if(-1 == gpio_setup_outputs() || -1 == gpio_setup_inputs()) ERRX("Can't setup GPIO");
|
||||
#ifndef SERVER
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
SSL_library_init();
|
||||
SSL_CTX *ctx = InitCTX();
|
||||
fd = OpenConn(atoi(G.port));
|
||||
#ifdef SERVER
|
||||
serverproc(ctx, fd);
|
||||
#else
|
||||
clientproc(ctx, fd);
|
||||
#endif
|
||||
// newer reached
|
||||
#ifdef __arm__
|
||||
gpio_close();
|
||||
#endif
|
||||
close(fd);
|
||||
SSL_CTX_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int geterrcode(SSL *ssl, int errcode){
|
||||
int sslerr = SSL_get_error(ssl, errcode);
|
||||
if(SSL_ERROR_WANT_READ == sslerr ||
|
||||
SSL_ERROR_WANT_WRITE == sslerr) return 0; // empty call
|
||||
int sd = SSL_get_fd(ssl);
|
||||
if(sslerr != SSL_ERROR_ZERO_RETURN){
|
||||
LOGERR("SSL error %d @client %d", sslerr, sd);
|
||||
WARNX("SSL error %d @client %d", sslerr, sd);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief read_string - read '\n'-terminated string from SSL
|
||||
* @param ssl - SSL
|
||||
* @param buf - buffer for text
|
||||
* @param l - max buf length (including zero)
|
||||
* @return amount of bytes read or -1 if client disconnected
|
||||
*/
|
||||
int read_string(SSL *ssl, char *buf, int l){
|
||||
if(!ssl || l < 1) return 0;
|
||||
bzero(buf, l);
|
||||
int bytes = SSL_peek(ssl, buf, l);
|
||||
DBG("Peek: %d (bufsz %d); buf=%s", bytes, l, buf);
|
||||
if(bytes < 1){ // nothing to read or error
|
||||
return geterrcode(ssl, bytes);
|
||||
}
|
||||
if(bytes < l && buf[bytes-1] != '\n'){ // string not ready, no buffer overfull
|
||||
return -1; // wait a rest of string
|
||||
}
|
||||
bytes = SSL_read(ssl, buf, l);
|
||||
DBG("Read: %d", bytes);
|
||||
if(bytes < 1){ // error
|
||||
return geterrcode(ssl, bytes);
|
||||
}
|
||||
buf[bytes-1] = 0; // replace '\n' with 0
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#ifdef __arm__
|
||||
/**
|
||||
* @brief poll_gpio - GPIO polling
|
||||
* @param ssl - ssl array to write
|
||||
* @param nfd - amount of descriptors (+1 - starting frol ssls[1])
|
||||
*/
|
||||
void poll_gpio(SSL **ssls, int nfd, cmd_t *commands){
|
||||
static double t0 = 0.;
|
||||
if(dtime() - t0 < GPIO_POLL_INTERVAL) return;
|
||||
char buf[64];
|
||||
uint32_t up, down;
|
||||
t0 = dtime();
|
||||
if(gpio_poll(&up, &down) <= 0 || !up) return;
|
||||
for(cmd_t *c = commands; c->cmd; ++c){
|
||||
if(c->gpio != up) continue;
|
||||
sprintf(buf, "%s\n", c->cmd);
|
||||
int l = strlen(buf);
|
||||
if(nfd == 1){
|
||||
if(SSL_write(ssls[0], buf, l) <= 0) WARNX("SSL write error");
|
||||
}else{
|
||||
for(int i = nfd-1; i > 0; --i){
|
||||
if(SSL_write(ssls[i], buf, l) <= 0){
|
||||
WARNX("SSL write error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief handle_message - parser or client/server messages
|
||||
* @param msg - string command
|
||||
*/
|
||||
int handle_message(const char *msg, cmd_t *gpios){
|
||||
int ret = FALSE;
|
||||
for(cmd_t *c = gpios; c->cmd; ++c){
|
||||
if(strcmp(msg, c->cmd)) continue;
|
||||
DBG("set pin %d", c->gpio);
|
||||
#ifdef __arm__
|
||||
if(!gpio_set_output(pin)) LOGERR("Can't change state according to pin %d", pin);
|
||||
else{
|
||||
LOGMSG("%s gpio %d", act == 1 ? "Set" : "Reset", pin);
|
||||
verbose(1, "%s gpio %d", act == 1 ? "Set" : "Reset", pin);
|
||||
ret = TRUE;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
67
schlagbaum/sslsock.h
Normal file
67
schlagbaum/sslsock.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of the schlagbaum project.
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <arpa/inet.h> // inet_ntop
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h> // addrinfo
|
||||
#include <netinet/in.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#if ! defined CLIENT && ! defined SERVER
|
||||
#error "Define CLIENT or SERVER before including this file"
|
||||
#endif
|
||||
#if defined CLIENT && defined SERVER
|
||||
#error "Both CLIENT and SERVER defined"
|
||||
#endif
|
||||
|
||||
#define BACKLOG 10
|
||||
|
||||
// command protocol
|
||||
// open gate
|
||||
#define CMD_OPEN "open"
|
||||
// close
|
||||
#define CMD_CLOSE "close"
|
||||
// siren
|
||||
#define CMD_SIREN "siren"
|
||||
// LEDs
|
||||
#define CMD_LED0 "led0"
|
||||
#define CMD_LED1 "led1"
|
||||
// connection OK - ping message
|
||||
#define CMD_PING "ping"
|
||||
|
||||
// server sends ping each 5s
|
||||
#define PING_TIMEOUT (5.)
|
||||
|
||||
typedef struct{
|
||||
uint32_t gpio; // gpio in number
|
||||
const char *cmd; // text command
|
||||
} cmd_t;
|
||||
|
||||
int open_socket();
|
||||
int read_string(SSL *ssl, char *buf, int l);
|
||||
|
||||
int handle_message(const char *msg, cmd_t *gpios);
|
||||
#ifdef __arm__
|
||||
void poll_gpio(SSL **ssls, int nfd, cmd_t *commands);
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user