add serialsock

This commit is contained in:
Edward Emelianov 2022-01-19 17:31:04 +03:00
parent 786ed54d03
commit 9192ee09a3
78 changed files with 5244 additions and 0 deletions

23
NES_webiface/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Prerequisites
*.d
# Object files
*.o
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.so
*.so.*
# qt-creator
*.config
*.cflags
*.cxxflags
*.creator*
*.files
*.includes

View File

@ -0,0 +1,71 @@
cmake_minimum_required(VERSION 3.0)
set(PROJ NES_web)
set(MINOR_VERSION "1")
set(MID_VERSION "0")
set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C)
message("VER: ${VERSION}")
# default flags
set(CMAKE_C_FLAGS_RELEASE "")
set(CMAKE_C_FLAGS_DEBUG "")
set(CMAKE_C_FLAGS "-O2 -std=gnu99")
set(CMAKE_COLOR_MAKEFILE ON)
# here is one of two variants: all .c in directory or .c files in list
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
# cmake -DDEBUG=1 -> debugging
if(DEFINED EBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wall -Werror -W")
set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_VERBOSE_MAKEFILE "ON")
add_definitions(-DEBUG)
else()
set(CMAKE_BUILD_TYPE RELEASE)
endif()
#pthreads
find_package(Threads REQUIRED)
###### pkgconfig ######
# pkg-config modules (for pkg-check-modules)
set(MODULES usefull_macros sqlite3)
# find packages:
find_package(PkgConfig REQUIRED)
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
###### additional flags ######
list(APPEND ${PROJ}_LIBRARIES "-lonion -lcrypt")
# change wrong behaviour with install prefix
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
else()
message("Change default install path to /usr/local")
set(CMAKE_INSTALL_PREFIX "/usr/local")
endif()
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
# exe file
add_executable(${PROJ} ${SOURCES})
# -I
include_directories(${${PROJ}_INCLUDE_DIRS})
# -L
link_directories(${${PROJ}_LIBRARY_DIRS})
# -D
add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
# -l
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} -lm)
# Installation of the program
INSTALL(TARGETS ${PROJ} DESTINATION "bin")

5
NES_webiface/HOWTO.cert Normal file
View File

@ -0,0 +1,5 @@
create:
openssl req -newkey rsa:2048 -nodes -keyout cert.key -x509 -days 365 -out cert.pem
review:
openssl x509 -text -noout -in cert.pem

3
NES_webiface/README.md Normal file
View File

@ -0,0 +1,3 @@
# NES_web
Web-interface for NES (Nesmith Echelle Spectrograph) management. Based on pusirobot and liboninon.

682
NES_webiface/auth.c Normal file
View File

@ -0,0 +1,682 @@
/*
* This file is part of the NES_web project.
* Copyright 2020 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 <crypt.h>
#include <inttypes.h>
#include <onion/dict.h>
#include <onion/log.h>
#include <onion/types_internal.h>
#include <pthread.h>
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "auth.h"
extern char *onion_sessions_generate_id();
extern void onion_random_init();
typedef struct{
sqlite3 *db; // session database
char *name; // database filename
sqlite3_stmt *add; // template to add/modify session
sqlite3_stmt *getsid; // get data by session ID
sqlite3_stmt *getsockid;// get data by socked ID
sqlite3_stmt *delold; // delete all old sessions (with atime < XX)
sqlite3_stmt *del; // delete session
pthread_mutex_t mutex; // locking mutex
} _sessionDB;
typedef struct{
sqlite3 *db; // auth database
char *name; // database filename
sqlite3_stmt *add; // add user data
sqlite3_stmt *get; // get user data by username
sqlite3_stmt *del; // delete user data
pthread_mutex_t mutex; // locking mutex
} _authDB;
static _sessionDB *sessionDB = NULL;
static _authDB *authDB = NULL;
/**
* @brief getQdata - find data in POST or GET parts
* @param req - request
* @param key - searching key
* @return value or NULL
*/
const char *getQdata(onion_request *req, const char *key){
if(!req || !key) return NULL;
printf("key %s, ", key);
const char *data = onion_request_get_query(req, key);
printf("GET: '%s' ", data);
if(!data){
data = onion_request_get_post(req, key);
if(data && *data == 0) data = NULL;
printf("POST: '%s'", data);
}
printf("\n");
fflush(stdout);
return data;
}
/**
* @brief sqprep - prepare SQLite statement
* @param db - database
* @param str - text string for statement
* @param stmt - the statement
* @return 0 if all OK
*/
static int sqprep(sqlite3 *db, const char *str, sqlite3_stmt **stmt){
int rc = sqlite3_prepare_v2(db, str, (int)strlen(str), stmt, NULL);
if(rc != SQLITE_OK){
WARNX("Can't prepare statement to save (%d)", rc);
return 1;
}
return 0;
}
/**
* @brief addtext - bind text phrase to statement
* @param stmt - the statement
* @param narg - argument number for binding
* @param str - the text itself
* @return 0 if all OK
*/
static int addtext(sqlite3_stmt *stmt, int narg, const char *str){
if(str == NULL) str = "";
int rc = sqlite3_bind_text(stmt, narg, str, -1, SQLITE_TRANSIENT);
if(rc != SQLITE_OK){
WARNX("Can't bind %s: %d", str, rc);
return 1;
}
return 0;
}
/**
* @brief addWSkey - add new websocket ID to session & send this ID to user
* @param res - web response
* @param session - session information to modify
*/
void addWSkey(onion_response *res, sessinfo *session){
if(!res || !session) return;
do{
FREE(session->sockID);
session->sockID = onion_sessions_generate_id();
DBG("Try to modify session %s", session->sessID);
if(!addSession(session, 1)){
DBG("Modify session: ID=%s, sockid=%s, atime=%" PRId64 ", user=%s, data=%s\n",
session->sessID, session->sockID, session->atime, session->username, session->data);
break;
}
}while(1);
onion_response_write0(res, session->sockID);
}
/**
* @brief qookieSession - find session ID from SESSION_COOKIE_NAME and search session in DB
* @param req - web request
* @return session data (allocated here) or NULL if session not found
*/
sessinfo *qookieSession(onion_request *req){
if(!req) return NULL;
/*const char *path = onion_request_get_path(req);
printf("Ask path %s\n", path);*/
onion_dict *cookies = onion_request_get_cookies_dict(req);
const char *skey = onion_dict_get(cookies, SESSION_COOKIE_NAME);
if(!skey){
WARNX("No session cookie found\n");
return NULL;
}
return getSession(skey);
}
onion_connection_status auth(_U_ onion_handler *h, onion_request *req, onion_response *res){
if(!req || !res) return OCS_CLOSE_CONNECTION;
if(onion_request_get_flags(req) & OR_HEAD) {
onion_response_write_headers(res);
return OCS_PROCESSED;
}
const char *host = onion_request_get_client_description(req);
char json[2048]; // json buffer for UA & IP
const char *UA = onion_request_get_header(req, "User-Agent");
snprintf(json, 2048, "{\"User-Agent\": \"%s\", \"User-IP\": \"%s\"}", UA, host);
DBG("Client: %s, UA: %s\n", host, UA);
const char *logout = getQdata(req, "LogOut");
DBG("logout=%s\n", logout);
sessinfo *session = qookieSession(req);
if(!session) DBG("No cookie, need to create\n");
else if(!logout){
onion_response_write0(res, AUTH_ANS_AUTHOK);
goto closeconn;
}
const char *username = NULL, *passwd = NULL;
if(logout){
DBG("User logged out\n");
if(session){
if(deleteSession(session->sessID))
WARNX("Can't delete session with ID=%s from database", session->sessID);
}
onion_response_write0(res, AUTH_ANS_LOGOUT);
onion_response_add_cookie(res, SESSION_COOKIE_NAME, "clear", 0, "/", NULL, OC_HTTP_ONLY|OC_SECURE);
goto closeconn;
}else{ // log in
freeSessInfo(&session);
username = getQdata(req, "login");
if(!username){
ONION_WARNING("no login field -> need auth");
onion_response_write0(res, AUTH_ANS_NEEDAUTH);
return OCS_CLOSE_CONNECTION;
}
passwd = getQdata(req, "passwd");
if(!passwd){
ONION_WARNING("Trying to enter authenticated area without password");
onion_response_write0(res, AUTH_ANS_NOPASSWD);
return OCS_FORBIDDEN;
}
}
userinfo *U = getUserData(username);
if(!U){
WARNX("User %s not found", username);
return OCS_FORBIDDEN;
}
char *pass = strdup(crypt(passwd, "$6$"));
if(!pass){
WARN("Error in ctypt or strdup");
freeUserInfo(&U);
return OCS_FORBIDDEN;
}
int comp = strcmp(pass, U->password);
freeUserInfo(&U);
FREE(pass);
if(comp){
WARNX("User %s give wrong password", username);
return OCS_FORBIDDEN;
}
session = MALLOC(sessinfo, 1);
session->atime = time(NULL);
session->username = strdup(username);
/*
onion_dict *data = onion_dict_new();
onion_dict_add(data, "UA", UA, 0);
onion_dict_add(data, "IP", host, 0);
onion_block *bl = onion_dict_to_json(data);
if(bl){
const char *json = onion_block_data(bl);
if(json) session->data = strdup(json);
}*/
session->data = strdup(json);
do{
FREE(session->sessID);
session->sessID = onion_sessions_generate_id();
DBG("Try to add session %s", session->sessID);
if(!addSession(session, 0)){
DBG("New session: ID=%s, atime=%" PRId64 ", user=%s, data=%s\n",
session->sessID, session->atime, session->username, session->data);
break;
}
sleep(2);
}while(1);
onion_response_add_cookie(res, SESSION_COOKIE_NAME, session->sessID, 366*86400, "/", NULL, OC_HTTP_ONLY|OC_SECURE);
onion_response_write0(res, AUTH_ANS_AUTHOK);
closeconn:
freeSessInfo(&session);
return OCS_CLOSE_CONNECTION;
}
/**
* @brief opendb - open SQLite database
* @param name - database filename
* @param db - database itself
* @return 0 if all OK
*/
static int opendb(const char *name, sqlite3 **db){
if(!name || !db) return 2;
if(SQLITE_OK != sqlite3_open(name, db)){
ONION_ERROR("Can't open database: %s", sqlite3_errmsg(*db));
sqlite3_close(*db);
return 1;
}
return 0;
}
/**
* @brief close_sessDB - close session database & free memory
*/
static void close_sessDB(){
if(!sessionDB) return;
pthread_mutex_lock(&sessionDB->mutex);
sqlite3_finalize(sessionDB->add);
sqlite3_finalize(sessionDB->del);
sqlite3_finalize(sessionDB->getsid);
sqlite3_finalize(sessionDB->getsockid);
sqlite3_close(sessionDB->db);
pthread_mutex_unlock(&sessionDB->mutex);
pthread_mutex_destroy(&sessionDB->mutex);
FREE(sessionDB->name);
FREE(sessionDB);
}
/**
* @brief close_authDB - close user database & free memory
*/
static void close_authDB(){
if(!authDB) return;
pthread_mutex_lock(&authDB->mutex);
sqlite3_finalize(authDB->add);
sqlite3_finalize(authDB->del);
sqlite3_finalize(authDB->get);
sqlite3_close(authDB->db);
pthread_mutex_unlock(&authDB->mutex);
pthread_mutex_destroy(&authDB->mutex);
FREE(authDB->name);
FREE(authDB);
}
/**
* @brief closeSQLite - close all databases
*/
void closeSQLite(){
close_authDB();
close_sessDB();
}
/**
* @brief initSQLite - init SQL databases
* @param auth_filename - database with auth data (username, password hash, access level)
* @param sess_filename - session database (session ID, websosket ID, access time, data {UA, IP, username, access level - JSON})
* @return error code or 0 if OK
*/
int initSQLite(const char *auth_filename, const char *sess_filename){
if(!auth_filename || !sess_filename) return 5;
int rc;
char *zErrMsg = NULL;
onion_random_init();
closeSQLite();
/******************************* users database *******************************/
authDB = MALLOC(_authDB, 1);
if(opendb(auth_filename, &authDB->db)) return 2;
authDB->name = strdup(auth_filename);
rc = sqlite3_exec(authDB->db, "CREATE TABLE passwd (user TEXT PRIMARY KEY NOT NULL, passhash TEXT, level INT, comment TEXT)", NULL, NULL, &zErrMsg);
if(rc != SQLITE_OK){
if(strcmp(zErrMsg, "table passwd already exists")){
WARNX("SQL CREATE error %d: %s", rc, zErrMsg);
goto ret;
}
}else{
fprintf(stderr, "Add default user\n");
// default admin: toor, password: p@ssw0rd
rc = sqlite3_exec(authDB->db, "INSERT INTO passwd VALUES (\"toor\", \"$6$$F55h0JlnNG19zS6YHUDX6h4zksblEBCFRqzCoCt6Y3VqE7zBeM/ifdGbUNMK.dLIwu9jq3fCmLBYEpzEpNCej0\", 0, \"\")", NULL, NULL, &zErrMsg);
if(rc != SQLITE_OK){
fprintf(stderr, "SQL INSERT error %d: %s\n", rc, zErrMsg);
sqlite3_free(zErrMsg);
goto ret;
}
}
// get user information
if(sqprep(authDB->db, "SELECT passhash, level, comment FROM passwd WHERE user=?",
&authDB->get)) goto ret;
// add or modify user
if(sqprep(authDB->db, "INSERT OR REPLACE INTO passwd (user, passhash, level, comment) VALUES (?, ?, ?, ?)",
&authDB->add)) goto ret;
// delete user
if(sqprep(authDB->db, "DELETE FROM passwd WHERE user=?", &authDB->del)) goto ret;
if(pthread_mutex_init(&authDB->mutex, NULL)){
WARN("Can't init authDB mutex");
goto ret;
}
/****************************** session database ******************************/
sessionDB = MALLOC(_sessionDB, 1);
if(opendb(sess_filename, &sessionDB->db)) goto ret;
sessionDB->name = strdup(sess_filename);
rc = sqlite3_exec(sessionDB->db, "CREATE TABLE sessions (sessID TEXT PRIMARY KEY NOT NULL, sockID TEXT, atime INT, username TEXT, data TEXT)", NULL, NULL, &zErrMsg);
if(rc != SQLITE_OK){
if(strcmp(zErrMsg, "table sessions already exists")){
WARNX("SQL CREATE error %d: %s", rc, zErrMsg);
sqlite3_free(zErrMsg);
goto ret;
}
}
// get session information
if(sqprep(sessionDB->db, "SELECT sockID, atime, username, data FROM sessions WHERE sessID=?",
&sessionDB->getsid)) goto ret;
if(sqprep(sessionDB->db, "SELECT sessID, atime, username, data FROM sessions WHERE sockID=?",
&sessionDB->getsockid)) goto ret;
// add or modify session
if(sqprep(sessionDB->db, "INSERT OR REPLACE INTO sessions (sessID, sockID, atime, username, data) VALUES (?, ?, ?, ?, ?)",
&sessionDB->add)) goto ret;
// delete session
if(sqprep(sessionDB->db, "DELETE FROM sessions WHERE sessID=?",
&sessionDB->del)) goto ret;
if(sqprep(sessionDB->db, "DELETE FROM sessions WHERE atime<?",
&sessionDB->delold)) goto ret;
return 0;
ret:
closeSQLite();
return -1;
}
/**
* @brief getUserData - find all records in authDB for given username
* @param username - user name
* @return userinfo structure allocated here or NULL if user not found
*/
userinfo *getUserData(const char *username){
if(!username) return NULL;
userinfo *U = NULL;
if(!authDB){
WARNX("User database not initialized");
return NULL;
}
pthread_mutex_lock(&authDB->mutex);
sqlite3_reset(authDB->get);
if(addtext(authDB->get, 1, username)) return NULL;
if(SQLITE_ROW != sqlite3_step(authDB->get)){
WARNX("User %s not found", username);
goto ret;
}
const char *pass = (const char *) sqlite3_column_text(authDB->get, 0);
int levl = sqlite3_column_int(authDB->get, 1);
const char *comment = (const char *) sqlite3_column_text(authDB->get, 2);
U = (userinfo *) malloc(sizeof(userinfo));
if(!U) goto ret;
U->username = strdup(username);
U->password = strdup(pass);
U->level = levl;
U->comment = strdup(comment);
ret:
pthread_mutex_unlock(&authDB->mutex);
return U;
}
/**
* @brief freeUserInfo - free memory for struct userinfo
* @param x - pointer to userinfo pointer
*/
void freeUserInfo(userinfo **x){
if(!x || !*x) return;
userinfo *U = *x;
free(U->username);
free(U->password);
free(U->comment);
free(U);
*x = NULL;
}
// callback for showAllUsers()
static int selallcb(_U_ void *notused, int argc, char **argv, _U_ char **no){
if(argc != 4){
fprintf(stderr, "Wrong argc: %d\n", argc);
return 1;
}
printf("%s\t%s\t%s\t%s\n", argv[0], argv[2], argv[1], argv[3]);
return 0;
}
/**
* @brief showAllUsers - printout user database
*/
void showAllUsers(){
char *zErrMsg = NULL;
green("\nUSER\tLevel\tPassHash\t\t\tComment\n");
pthread_mutex_lock(&authDB->mutex);
int rc = sqlite3_exec(authDB->db, "SELECT * FROM passwd GROUP BY user", selallcb, NULL, &zErrMsg);
if(rc != SQLITE_OK){
WARNX("SQL CREATE error %d: %s", rc, zErrMsg);
sqlite3_free(zErrMsg);
}
pthread_mutex_unlock(&authDB->mutex);
}
/**
* @brief deleteUser - delete user record from database
* @param username - user name
* @return 0 if all OK
*/
int deleteUser(const char *username){
if(!username) return 1;
userinfo *u = getUserData(username);
if(!u) return 1;
FREE(u);
pthread_mutex_lock(&authDB->mutex);
sqlite3_reset(authDB->del);
if(addtext(authDB->del, 1, username)) goto reterr;
if(SQLITE_DONE != sqlite3_step(authDB->del)) goto reterr;
else{
printf("User %s deleted\n", username);
pthread_mutex_unlock(&authDB->mutex);
return 0;
}
reterr:
pthread_mutex_unlock(&authDB->mutex);
WARNX("Can't delete user %s", username);
return 2;
}
/**
* @brief addUser - add user to database
* @param User - struct with user data
* @return 0 if OK
*/
int addUser(userinfo *User){
if(!User) return 1;
if(strlen(User->username) < 1){
WARNX("No username");
return 1;
}
pthread_mutex_lock(&authDB->mutex);
sqlite3_reset(authDB->add);
if(addtext(authDB->add, 1, User->username)) goto reterr;
if(addtext(authDB->add, 2, User->password)) goto reterr;
if(SQLITE_OK != sqlite3_bind_int(authDB->add, 3, User->level)) goto reterr;
addtext(authDB->add, 4, User->comment);
if(SQLITE_DONE == sqlite3_step(authDB->add)){
green("Add to database:\n");
printf("\tuser %s, passHash %s, level %d, comment '%s'\n",
User->username, User->password, User->level, User->comment);
pthread_mutex_unlock(&authDB->mutex);
return 0;
}
reterr:
pthread_mutex_unlock(&authDB->mutex);
WARNX("Can't insert user %s", User->username);
return 1;
}
/**
* @brief getSession - find session by session ID or socket ID
* @param ID - session or socket ID
* @return NULL if not found or session structure (allocated here)
*/
sessinfo *getSession(const char *ID){
if(!ID) return NULL;
sqlite3_stmt *stmt = sessionDB->getsid;
sessinfo *s = NULL;
pthread_mutex_lock(&sessionDB->mutex);
sqlite3_reset(stmt);
if(addtext(stmt, 1, ID)) goto ret;
const char *sessID = NULL, *sockID = NULL;
if(SQLITE_ROW != sqlite3_step(stmt)){
stmt = sessionDB->getsockid;
sqlite3_reset(stmt);
if(addtext(stmt, 1, ID)) goto ret;
if(SQLITE_ROW != sqlite3_step(stmt)){
WARNX("Session %s not found\n", ID);
goto ret;
}else{
sockID = ID;
sessID = (const char *) sqlite3_column_text(stmt, 0);
}
}else{
sessID = ID;
sockID = (const char *) sqlite3_column_text(stmt, 0);
}
// 0-ID, 1-atime, 2-username, 3-data
int64_t atime = sqlite3_column_int64(stmt, 1);
const char *username = (const char *)sqlite3_column_text(stmt, 2);
const char *data = (const char *)sqlite3_column_text(stmt, 3);
s = (sessinfo*) malloc(sizeof(sessinfo));
if(!s) goto ret;
s->sessID = strdup(sessID);
s->sockID = strdup(sockID);
s->atime = atime;
s->username = strdup(username);
s->data = strdup(data);
ret:
pthread_mutex_unlock(&sessionDB->mutex);
return s;
}
/**
* @brief addSession - add new session or modify old
* @param s - structure with session information
* @param modify != 0 to modify existant session
* @return 0 if all OK
*/
int addSession(sessinfo *s, int modify){
if(!s) return 1;
sessinfo *byID = getSession(s->sessID);
sessinfo *bysID = getSession(s->sockID);
if(byID || bysID){ // found session with same IDs
if(modify){
if(bysID && strcmp(bysID->sessID, s->sessID)){
freeSessInfo(&byID);
freeSessInfo(&bysID);
WARNX("Found another session with the same sockID");
return 2;
}
}else{
if(byID) WARNX("Found another session with the same sessID");
if(bysID) WARNX("Found another session with the same sockID");
freeSessInfo(&byID);
freeSessInfo(&bysID);
return 3;
}
}
pthread_mutex_lock(&sessionDB->mutex);
// 1-sessID, 2-sockID, 3-atime, 4-username, 5-data
sqlite3_reset(sessionDB->add);
if(addtext(sessionDB->add, 1, s->sessID)) goto reterr;
if(addtext(sessionDB->add, 2, s->sockID)) goto reterr;
if(SQLITE_OK != sqlite3_bind_int64(sessionDB->add, 3, s->atime)) goto reterr;
if(addtext(sessionDB->add, 4, s->username)) goto reterr;
if(addtext(sessionDB->add, 5, s->data)) goto reterr;
if(SQLITE_DONE == sqlite3_step(sessionDB->add)){
pthread_mutex_unlock(&sessionDB->mutex);
return 0;
}
reterr:
pthread_mutex_unlock(&sessionDB->mutex);
WARNX("Can't insert session %s\n", s->sessID);
return 1;
}
/**
* @brief deleteSession - delete session by SessID
* @param sessID - session ID
* @return 0 if all OK
*/
int deleteSession(const char *sessID){
if(!sessID) return 1;
pthread_mutex_lock(&sessionDB->mutex);
sqlite3_reset(sessionDB->del);
if(addtext(sessionDB->del, 1, sessID)) goto reterr;
if(SQLITE_DONE != sqlite3_step(sessionDB->del)) goto reterr;
else{
printf("Session with id %s deleted\n", sessID);
pthread_mutex_unlock(&sessionDB->mutex);
return 0;
}
reterr:
pthread_mutex_unlock(&sessionDB->mutex);
WARNX("Can't delete session with ID %s\n", sessID);
return 1;
}
/**
* @brief deleteSession - delete session by SessID
* @param minatime - minimal access time (all sessions with atime<minatime will be deleted)
* @return 0 if all OK
*/
int deleteOldSessions(int64_t minatime){
pthread_mutex_lock(&sessionDB->mutex);
sqlite3_reset(sessionDB->delold);
if(SQLITE_OK != sqlite3_bind_int64(sessionDB->delold, 1, minatime)) goto reterr;
if(SQLITE_DONE != sqlite3_step(sessionDB->delold)) goto reterr;
else{
pthread_mutex_unlock(&sessionDB->mutex);
return 0;
}
reterr:
pthread_mutex_unlock(&sessionDB->mutex);
WARNX("Can't delete sessions with atime < %" PRId64 " \n", minatime);
return 1;
}
// callback for showAllSessions
static int selallscb(void _U_ *notused, int argc, char **argv, char _U_ **no){
if(argc != 5){
fprintf(stderr, "Wrong argc: %d\n", argc);
return 1;
}
printf("%s\t%s\t%s\t%s\t%s\n", argv[3], argv[2], argv[0], argv[1], argv[4]);
return 0;
}
void showAllSessions(){
char *zErrMsg = NULL;
green("Username\tAtime\tSession ID\t\tSocket ID\t\tData\n");
int rc = sqlite3_exec(sessionDB->db, "SELECT * FROM sessions ORDER BY username", selallscb, NULL, &zErrMsg);
if(rc != SQLITE_OK){
WARNX("SQL CREATE error %d: %s\n", rc, zErrMsg);
sqlite3_free(zErrMsg);
}
}
/**
* @brief freeSessInfo - free session info structure
* @param si - address of pointer to SI
*/
void freeSessInfo(sessinfo **si){
if(!si || !*si) return;
sessinfo *s = *si;
free(s->data);
free(s->sessID);
free(s->sockID);
free(s->username);
free(*si);
*si = NULL;
}
/**
* @brief vacuum - rebuild databases to free all deleted records
*/
void vacuumSQLite(){
char *zErrMsg = NULL;
int rc = sqlite3_exec(authDB->db, "vacuum", NULL, NULL, &zErrMsg);
if(rc != SQLITE_OK){
WARNX("Error in vacuum %s (%d): %s\n", authDB->name, rc, zErrMsg);
sqlite3_free(zErrMsg);
}
rc = sqlite3_exec(sessionDB->db, "vacuum", NULL, NULL, &zErrMsg);
if(rc != SQLITE_OK){
WARNX("Error in vacuum %s (%d): %s\n", sessionDB->name, rc, zErrMsg);
sqlite3_free(zErrMsg);
}
green("Both databases are rebuilt\n");
}

70
NES_webiface/auth.h Normal file
View File

@ -0,0 +1,70 @@
/*
* This file is part of the NES_web project.
* Copyright 2020 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
#ifndef AUTH_H__
#define AUTH_H__
#include <onion/onion.h>
#define SESSION_COOKIE_NAME "Acookie"
// standard answers to client
#define AUTH_ANS_NEEDAUTH "NeedAuth"
#define AUTH_ANS_AUTHOK "AuthOK"
#define AUTH_ANS_LOGOUT "LogOut"
#define AUTH_ANS_NOPASSWD "NoPassword"
typedef struct{
char *username; // user name
char *password; // password hash (SHA512)
int level; // user access level
char *comment; // optional comment
} userinfo;
typedef struct{
char *sessID; // session ID
char *sockID; // websoket ID (cleared when disconnected or after 1 minute after `atime`)
int64_t atime; // last access time (UNIX time) - all ID data cleared after 1yr of inactivity
char *username; // username
char *data; // JSON data with UA, IP etc
} sessinfo;
const char *getQdata(onion_request *req, const char *key);
onion_connection_status auth(onion_handler *h, onion_request *req, onion_response *res);
void addWSkey(onion_response *res, sessinfo *session);
int initSQLite(const char *auth_filename, const char *sess_filename);
void closeSQLite();
void vacuumSQLite();
userinfo *getUserData(const char *username);
void freeUserInfo(userinfo **x);
void showAllUsers();
int deleteUser(const char *username);
int addUser(userinfo *User);
sessinfo *getSession(const char *ID);
sessinfo *qookieSession(onion_request *req);
int addSession(sessinfo *s, int modify);
int deleteSession(const char *sessID);
int deleteOldSessions(int64_t minatime);
void showAllSessions();
void freeSessInfo(sessinfo **si);
#endif // AUTH_H__

28
NES_webiface/cert.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC68f94gnFpcepC
+kpD4bxY7sQAXYbyp5ahAWun4scbLJPjJe56/CE47yvTpdU8KrVfHLYmP8KcF4pM
w0ZUNMCEenkQkb+eWjIviw3wzgesqLSssM+GZzlg92V2xkYlS/hEolUh0Z+Gl1Se
zl9f1S/EhL6RTfQx5ZuGlppHknqtbP0g4QNkxPXGj/qXqmaWLOxb8fX30eNf/6SF
vl1o45xwFE1SSGQnyh8KKZUWAPH2aZWpwwgB8gv4XwaVWo7DirbqOlUhhkylhhl0
cA7dHFgODTLluRstYr9M5u3WdoJzTGZTvg2ZV1vmfpQMVbat49TVJYZI3iLKoBHQ
IzeSU/lvAgMBAAECggEAUlc40RmTXoBgUHPxtgh9byZrikWnpMWQIQaBJodKb3uo
/8m7Ssw2zd76jNRkIYYmMOhyilJXI21y6vCvz3MUwMU5AcVQgyzzIeG7mC8HTlNY
kR+nqGla6ozNUg1u5AqcJY7itGyiOSP6j6ASfiFmUsatMU8GmduqLxOyjIfGJRA1
hnNAqYsVv2LbhbUQJTBozYMz2XYfiq/GbzL2NC1tv/xWoSL4zELjDuiGyZcT8oyV
u+dOf0x/VXOLvHqDrA8HuFbUrBjK5z0QW9iYReti/oxuhGrlp2rJLuzivKr4l+v8
qA0momNxhFFgyyx2Mb5zxp9TwA2x7q5Cg3vOCje3MQKBgQDe6MTywxuO4MVpwDSl
yot8vzUlI7Bv8BtdW18JUc5y1SIyGHlW1cuaQnZW6hbor6UxAoWi7oIjaFX+JQVD
3NLEnjuUF64/Y+k8wtvK0fFeZ2nvx2UNHF50JV0tQZtHo8znwKI/L8ZBSGJcBwh2
BGXtJtK9AY6C5xv5U03xRukw/QKBgQDWsn0qd6mV6iGqLth6sjDOJt/RFrvcikXb
lMhkCyGPuR2lQsUDbgWMcb/td3CqR5iZAksDbXrPqQBZVm9LO7DfDsL9GxtfgMrt
0dWk0Baod/n4NQBn8A6qGpbheFZwOhNGayYI17IqtRJmfLJuR7gRvm/IfGKso24X
TTA3kpSl2wKBgEBv52cJ8bB213p/fniiuXnhSDqpO3rQXQi6vhlSlaxqYk069/Cb
MxUvu0faua6f/8/QG9OCwQn9QkaKayA3+JGv8CcaRVu7xRO0fJb/45dXq68N4+9L
UR6gInRPr9SgzD3+WKiNZfE/PHe/7Lk5AkHw5CCRD6JVrqd/ZlumFQj9AoGBAKae
fM7xcRYcTyYRFwYZthC3UKmnOAJO+SoRTHd/v/sXUe+IYvdncjztpmK3eCNeTwoo
Imk1lMMGSHQMxXCgkYJ6pU7is5qpjFOGroQqzfrOqZs8HuWLAwZ2fjPbPVH5cC4N
R8ZDB01nmzEYgy1c0XhLz9rK1ZVffDfvOoVWZ7BTAoGAdQUkTWTTHU+Xwc9HshJy
qDFAFu5SvvO2dn3OvbGVUVc+I3M1P+HMeKS3HnomFLtRRDUGEbqZtPfrxBRnduW9
cEtgO+smyq86vs1p/ohmUESB2ablkvOgwXTXus+NsmUt3uVDq+0lFTfog/DX49+O
EiTvSXB1U92Opv/kh8ZhQak=
-----END PRIVATE KEY-----

21
NES_webiface/cert.pem Normal file
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUU3mUrQDJVG5A00d+WvVFfqScrqIwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA3MTMxNjE0MTlaFw0yMTA3
MTMxNjE0MTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQC68f94gnFpcepC+kpD4bxY7sQAXYbyp5ahAWun4scb
LJPjJe56/CE47yvTpdU8KrVfHLYmP8KcF4pMw0ZUNMCEenkQkb+eWjIviw3wzges
qLSssM+GZzlg92V2xkYlS/hEolUh0Z+Gl1Sezl9f1S/EhL6RTfQx5ZuGlppHknqt
bP0g4QNkxPXGj/qXqmaWLOxb8fX30eNf/6SFvl1o45xwFE1SSGQnyh8KKZUWAPH2
aZWpwwgB8gv4XwaVWo7DirbqOlUhhkylhhl0cA7dHFgODTLluRstYr9M5u3WdoJz
TGZTvg2ZV1vmfpQMVbat49TVJYZI3iLKoBHQIzeSU/lvAgMBAAGjUzBRMB0GA1Ud
DgQWBBQiHARml0EswF90cHiLYLKxpgj3DjAfBgNVHSMEGDAWgBQiHARml0EswF90
cHiLYLKxpgj3DjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB4
mMeSGGA4GAfJnxIwRJWGhj2KoweOh9UOVUtvrEJm3LoURbElo1pV3Q3vzt79wLn5
JPdXyN/MexYhtq7cwKQps3E4FElmIGUbR4peO/M1z3wJLaXSbaZqeo7ogSW6vzZL
xIHuxLtchNOi4rkSmOhzJqJ23cLULIF7Z94nwxXRxqKaIW8hXB7mlV9cDztpn2sz
K58M9VYmlfZetoHDzmsymM2DTmmoqR28Ykz+wK+G4U6W1+s7sY3Q+mG91lwvpzTf
yvz6dEtqi8ktBdu86E0yHfX04oDyKtl40d0LDzQT5JWhKQFiC25W5tE2p8QLbtHz
D5vg23HjTgRHZ80+3e4J
-----END CERTIFICATE-----

150
NES_webiface/cmdlnopts.c Normal file
View File

@ -0,0 +1,150 @@
/*
* This file is part of the NES_web project.
* Copyright 2020 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 <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "cmdlnopts.h"
/*
* here are global parameters initialisation
*/
static int help;
// global parameters (init with default):
glob_pars G = {
.port = "8080",
.wsport = "8081",
.certfile = "cert.pem",
.keyfile = "cert.key",
};
/*
* 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")},
{"vacuum", NO_ARGS, NULL, 'v', arg_int, APTR(&G.vacuum), _("make \"vacuum\" operation with databases")},
{"dumpusers", NO_ARGS, NULL, 'U', arg_int, APTR(&G.dumpUserDB),_("dump users database")},
{"dumpsess", NO_ARGS, NULL, 'S', arg_int, APTR(&G.dumpSessDB),_("dump session database")},
{"server", NO_ARGS, NULL, 'r', arg_int, APTR(&G.runServer), _("run server process")},
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("server port to listen")},
{"wsport", NEED_ARG, NULL, 'P', arg_string, APTR(&G.wsport), _("websocket port to listen (!= server port!)")},
{"certfile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.certfile), _("file with SSL certificate")},
{"keyfile", NEED_ARG, NULL, 'k', arg_string, APTR(&G.keyfile), _("file with SSL key")},
{"usersdb", NEED_ARG, NULL, 'u', arg_string, APTR(&G.usersdb), _("users database filename")},
{"sessiondb",NEED_ARG, NULL, 's', arg_string, APTR(&G.sessiondb), _("session database filename")},
{"userdel", MULT_PAR, NULL, 'd', arg_string, APTR(&G.userdel), _("user to delete (maybe several)")},
{"useradd", NO_ARGS, NULL, 'a', arg_int, APTR(&G.useradd), _("add user[s] interactively")},
{"sdatime", NEED_ARG, NULL, 'A', arg_longlong,APTR(&G.delatime), _("minimal atime to delete sessions from DB (-1 for >year)")},
{"sessdel", NEED_ARG, NULL, 'l', arg_string, APTR(&G.delsession),_("delete session by sessID or sockID")},
{"logfile", NEED_ARG, NULL, 'L', arg_string, APTR(&G.logfilename),_("log file name")},
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
*/
glob_pars *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){
G.rest_pars_num = argc;
G.rest_pars = MALLOC(char *, argc);
for (i = 0; i < argc; i++)
G.rest_pars[i] = strdup(argv[i]);
}
return &G;
}
// array with all opened logs - for error/warning messages
static Cl_log *errlogs = NULL;
static int errlogsnum = 0;
/**
* @brief Cl_createlog - create log file: init mutex, test file open ability
* @param log - log structure
* @return 0 if all OK
*/
int Cl_createlog(Cl_log *log){
if(!log || !log->logpath || log->loglevel >= LOGLEVEL_NONE) return 1;
FILE *logfd = fopen(log->logpath, "a");
if(!logfd){
WARN("Can't open log file");
return 2;
}
fclose(logfd);
if(pthread_mutex_init(&log->mutex, NULL)){
WARN("Can't init log mutes");
return 3;
}
errlogs = realloc(errlogs, (++errlogsnum) *sizeof(Cl_log));
if(!errlogs) errlogsnum = 0;
else memcpy(&errlogs[errlogsnum-1], log, sizeof(Cl_log));
return 0;
}
/**
* @brief Cl_putlog - put message to log file with/without timestamp
* @param timest - ==1 to put timestamp
* @param log - pointer to log structure
* @param lvl - message loglevel (if lvl > loglevel, message won't be printed)
* @param fmt - format and the rest part of message
* @return amount of symbols saved in file
*/
int Cl_putlogt(int timest, Cl_log *log, Cl_loglevel lvl, const char *fmt, ...){
if(!log || !log->logpath || log->loglevel >= LOGLEVEL_NONE) return 0;
if(lvl > log->loglevel) return 0;
if(pthread_mutex_lock(&log->mutex)){
WARN("Can't lock log mutex");
return 0;
}
int i = 0;
FILE *logfd = fopen(log->logpath, "a");
if(!logfd) goto rtn;
if(timest){
char strtm[128];
time_t t = time(NULL);
struct tm *curtm = localtime(&t);
strftime(strtm, 128, "%Y/%m/%d-%H:%M", curtm);
i = fprintf(logfd, "%s\t", strtm);
}
va_list ar;
va_start(ar, fmt);
i += vfprintf(logfd, fmt, ar);
va_end(ar);
fclose(logfd);
rtn:
pthread_mutex_unlock(&log->mutex);
return i;
}

69
NES_webiface/cmdlnopts.h Normal file
View File

@ -0,0 +1,69 @@
/*
* This file is part of the NES_web project.
* Copyright 2020 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
#ifndef CMDLNOPTS_H__
#define CMDLNOPTS_H__
#include <pthread.h>
/*
* here are some typedef's for global data
*/
typedef struct{
int vacuum; // make "vacuum" operation
int dumpUserDB; // dump users database
int dumpSessDB; // dump session database
int runServer; // run as server
int useradd; // add user[s]
char *port; // server port to listen
char *wsport; // websocket port to listen (!= port !!!)
char *certfile; // file with SSL certificate
char *keyfile; // file with SSL key
char *usersdb; // users database name
char *sessiondb; // session database name
char **userdel; // user names to delete
long long delatime; // minimal atime to delete sessions from DB
char *delsession; // delete session by sessID or sockID
char *logfilename; // name of log file
int rest_pars_num; // number of rest parameters
char** rest_pars; // the rest parameters: array of char*
} glob_pars;
extern glob_pars G;
glob_pars *parse_args(int argc, char **argv);
typedef enum{
LOGLEVEL_DBG, // all messages
LOGLEVEL_MSG, // all without debug
LOGLEVEL_WARN, // only warnings and errors
LOGLEVEL_ERR, // only errors
LOGLEVEL_NONE // no logs
} Cl_loglevel;
typedef struct{
char *logpath; // full path to logfile
Cl_loglevel loglevel; // loglevel
pthread_mutex_t mutex; // log mutex
} Cl_log;
int Cl_createlog(Cl_log *log);
int Cl_putlogt(int timest, Cl_log *log, Cl_loglevel lvl, const char *fmt, ...);
#endif // CMDLNOPTS_H__

250
NES_webiface/main.c Normal file
View File

@ -0,0 +1,250 @@
/*
* This file is part of the NES_web project.
* Copyright 2020 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 <crypt.h>
#include <errno.h>
#include <onion/block.h>
#include <onion/exportlocal.h>
#include <onion/handler.h>
#include <onion/log.h>
#include <onion/onion.h>
#include <onion/shortcuts.h>
#include <onion/types_internal.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "auth.h"
#include "cmdlnopts.h"
#include "websockets.h"
// temporary
#define putlog(...)
onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_response *res){
sessinfo *session = qookieSession(req);
if(!session){
onion_response_write0(res, "NeedAuth");
ONION_WARNING("try to run command without auth");
return OCS_FORBIDDEN;
}
const char *path = onion_request_get_path(req);
if(path && *path == 0) path = NULL;
else printf("GET ask path %s\n", path);
const char *param = getQdata(req, "param");
if(param) printf("User set param=%s\n", param);
if(getQdata(req, "getWSkey")){
addWSkey(res, session);
}
freeSessInfo(&session);
return OCS_CLOSE_CONNECTION;
}
static onion *os = NULL, *ow = NULL;
void signals(int signo){
closeSQLite();
if(os) onion_free(os);
if(ow) onion_free(ow);
exit(signo);
}
// POST/GET server
static void *runPostGet(_U_ void *data){
os = onion_new(O_THREADED);
if(!(onion_flags(os) & O_SSL_AVAILABLE)){
ONION_ERROR("SSL support is not available");
signals(1);
}
int error = onion_set_certificate(os, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
if(error){
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
signals(1);
}
onion_set_port(os, G.port);
onion_url *url = onion_root_url(os);
onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static"));
onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL);
onion_url_add(url, "^auth/", auth);
onion_url_add(url, "^get/", get);
error = onion_listen(os);
if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno));
onion_free(os);
return NULL;
}
// Websocket server
static void *runWS(_U_ void *data){
ow = onion_new(O_THREADED);
if(!(onion_flags(ow) & O_SSL_AVAILABLE)){
ONION_ERROR("SSL support is not available");
signals(1);
}
int error = onion_set_certificate(ow, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
if(error){
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
signals(1);
}
onion_set_port(ow, G.wsport);
onion_url *url = onion_root_url(ow);
onion_url_add(url, "", websocket_run);
DBG("Listen websocket");
error = onion_listen(ow);
if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno));
onion_free(ow);
return NULL;
}
static void runServer(){
// if(G.logfilename) Cl_createlog();
signal(SIGTERM, signals);
signal(SIGINT, signals);
signal(SIGQUIT, signals);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP, SIG_IGN);
pthread_t pg_thread, ws_thread;
if(pthread_create(&pg_thread, NULL, runPostGet, NULL)){
ERR("pthread_create()");
}
if(pthread_create(&ws_thread, NULL, runWS, NULL)){
ERR("pthread_create()");
}
do{
if(pthread_kill(pg_thread, 0) == ESRCH){ // POST/GET died
WARNX("POST/GET server thread died");
putlog("POST/GET server thread died");
pthread_join(pg_thread, NULL);
if(pthread_create(&pg_thread, NULL, runPostGet, NULL)){
putlog("pthread_create() failed");
ERR("pthread_create()");
}
}
if((pthread_kill(pg_thread, 0) == ESRCH) || (pthread_kill(ws_thread, 0) == ESRCH)){ // died
WARNX("Websocket server thread died");
putlog("Websocket server thread died");
pthread_join(ws_thread, NULL);
if(pthread_create(&ws_thread, NULL, runWS, NULL)){
putlog("pthread_create() failed");
ERR("pthread_create()");
}
}
#if 0
usleep(1000); // sleep a little or thread's won't be able to lock mutex
if(dtime() - tgot < T_INTERVAL) continue;
tgot = dtime();
/*
* INSERT CODE HERE
* Gather data (poll_device)
*/
// copy temporary buffers to main
pthread_mutex_lock(&mutex);
/*
* INSERT CODE HERE
* fill global data buffers
*/
pthread_mutex_unlock(&mutex);
#endif
}while(1);
putlog("Unreaceable code reached!");
ERRX("Unreaceable code reached!");
}
static char *getl(char *msg, int noempty){
static char *inpstr = NULL;
do{
FREE(inpstr);
printf("Enter %s: ", msg);
size_t n = 0;
if(getline(&inpstr, &n, stdin) < 0) FREE(inpstr);
else{
char *nl = strchr(inpstr, '\n');
if(nl) *nl = 0;
if(strlen(inpstr) < 1 && noempty){
WARNX("Enter non-empty string");
}else break;
}
}while(1);
return inpstr;
}
static void adduser(){
userinfo *User = MALLOC(userinfo, 1);
char *str;
do{
str = getl("username", 1);
printf("Username: %s\n", str);
User->username = strdup(str);
do{
str = getl("password", 1);
if(strlen(str) < 4){
WARNX("Bad password: not less than 4 symbols");
}else break;
}while(1);
User->password = strdup(crypt(str, "$6$"));
printf("PassHash: %s\n", User->password);
str = getl("access level", 1);
User->level = atoi(str);
printf("Level: %d\n", User->level);
str = getl("comment", 0);
if(strlen(str)){
User->comment = strdup(str);
printf("Comment: %s", User->comment);
}
if(addUser(User)) continue;
if(!(str = getl("next? (y/n)", 0)) || 0 != strcmp(str, "y")) break;
}while(1);
freeUserInfo(&User);
}
extern char *onion_sessions_generate_id();
int main(int argc, char **argv){
initial_setup();
parse_args(argc, argv);
if(!G.usersdb) ERRX("You should point users database file name");
if(!G.sessiondb) ERRX("You should point session database file name");
if(initSQLite(G.usersdb, G.sessiondb)) return 1;
if(G.vacuum){
printf("VAAAA\n");
vacuumSQLite();
}
if(G.dumpUserDB) showAllUsers();
if(G.userdel){
for(int i = 0; G.userdel[i]; ++i){
if(!deleteUser(G.userdel[i])) green("User %s deleted\n", G.userdel[i]);
}
}
if(G.useradd) adduser();
if(G.dumpSessDB) showAllSessions();
if(G.delsession){
if(!deleteSession(G.delsession))
green("Session with ID %s deleted\n", G.delsession);
}
if(G.delatime){
if(G.delatime < 0) G.delatime = time(NULL) - 31556926LL;
if(!deleteOldSessions((int64_t)G.delatime))
green("All sessions with atime<%lld deleted\n", G.delatime);
}
if(G.runServer){
if(strcmp(G.port, G.wsport) == 0) ERRX("Server port ans websocket port should be different!");
runServer();
}
closeSQLite();
return 0;
}

View File

@ -0,0 +1,156 @@
<html>
<head>
<style type="text/css">
.shadow{
display:none;
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
}
.midmsg{
position:fixed;
top:50%;
left:50%;
margin-left:auto;
margin-right:auto;
}
.C{text-align:center;}
.R{text-align:right;}
</style>
<script src="/pass.js" type="text/javascript" language="javascript"></script>
<script>
var user="", passwd="", level="", url="";
const CGI_PATH = "https://ishtar.sao.ru/cgi-bin/auth";
function parseErr(txt){
$('msg').innerHTML = "ïÛÉÂËÁ!<p></p>" + txt.replace("\n", "<br>");
setTimeout(function(){$('msg').innerHTML = "";}, 3500);
}
function sendrequest(request, req_STR, onOK){
var timeout_id
request = new XMLHttpRequest();
request.open("POST", CGI_PATH, true);
request.setRequestHeader("Accept-Charset", "koi8-r");
request.overrideMimeType("multipart/form-data; charset=koi8-r");
request.setRequestHeader("Cookie", document.cookie);
request.onreadystatechange=function(){
if (request.readyState == 4){
if (request.status == 200){
clearTimeout(timeout_id);
onOK(request);
}
else{
clearTimeout(timeout_id);
parseErr("ïÛÉÂËÁ ÐÅÒÅÄÁÞÉ ÚÁÐÒÏÓÁ. ðÏÐÒÏÂÕÊÔÅ ÅÝÅ ÒÁÚ.");
}
}
}
request.send(req_STR);
timeout_id = setTimeout(function(){parseErr("ôÁÊÍÁÕÔ ÐÅÒÅÄÁÞÉ ÚÁÐÒÏÓÁ. ðÏÐÒÏÂÕÊÔÅ ÅÝÅ ÒÁÚ.");}, 3000);
}
function run(){
var QS;
sendrequest(QS, "admin=lsusers", lsusers);
}
function lsusers(QS){
var ans = QS.responseText;
var pars = ans.split("\n");
var i, l, s;
for(i=0, l=pars.length; i < l; i++){
s = pars[i].split(";");
if(pars[i].length < 2) continue;
if(s.length != 3){
parseErr(ans+"<br>str="+s);
return;
}
addUsersString(s);
}
}
function addUsersString(str){
var ulist = $('userlist');
var div = document.createElement('div');
ulist.appendChild(div);
var u = str[0];
div.innerHTML = "<span>"+u+"</span> <span>"+str[1]+"</span> <span>"+str[2]+"</span> "+
"<span onclick='delUser(\""+u+"\");'>delete</span> "+
"<span onclick='editUser(\""+u+"\",\""+str[1]+"\",\""+str[2]+"\");'>edit</span>";
}
function useradd(arg){
function pair(name){
var str = "<div class='R'>"+name+"<input type=text id="+
name+" onchange=\""+name+"=$('"+name+"').value;\""+
" onblur=\""+name+"=$('"+name+"').value;\" value='"+eval(name)+"'></div>"
return str;
}
var div = document.createElement('div');
$("box").style.display = "block";
div.className = "midmsg";
$("box").appendChild(div);
div.innerHTML = "<div class='C'>äÏÂÁ×ÉÔØ/ÉÚÍÅÎÉÔØ ÐÏÌØÚÏ×ÁÔÅÌÑ</div>"+
pair("user")+pair("passwd")+pair("level")+pair("url")+
"<div class='C'><button onclick='addUser(\""+arg+"\");'>OK</button>"+
"<button onclick='cancelAdd();'>ïÔÍÅÎÁ</button></div>";
}
function chkAns(req, Msg){
if(req.responseText.length > 0)
parseErr(Msg);
else{
window.location.reload();
}
}
function addUser(arg){
var request;
function cantadd(req){
chkAns(req, "ÎÅ ÍÏÇÕ ÄÏÂÁ×ÉÔØ/ÒÅÄÁËÔÉÒÏ×ÁÔØ ÐÏÌØÚÏ×ÁÔÅÌÑ<br>"+req.responseText);
}
if(user==""){
parseErr("ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÌØÚÏ×ÁÔÅÌÑ");
return;
}
if(passwd==""){
parseErr("îÅ ÚÁÄÁÎ ÐÁÒÏÌØ ÐÏÌØÚÏ×ÁÔÅÌÑ");
return;
}
if(level=="" || Number(level) < 0){
parseErr("îÅ ÚÁÄÁÎ ÉÌÉ ÚÁÄÁÎ ÎÅ×ÅÒÎÏ ÕÒÏ×ÅÎØ ÄÏÓÔÕÐÁ ÐÏÌØÚÏ×ÁÔÅÌÑ");
return;
}
if(url==""){
parseErr("îÅ ÚÁÄÁÎ ÁÄÒÅÓ ÄÏÓÔÕÐÎÙÈ ÐÏÌØÚÏ×ÁÔÅÌÀ ÒÅÓÕÒÓÏ× (ÉÌÉ \"/\", ÅÓÌÉ ÄÏÓÔÕÐÎÏ ×ÓÅ)");
return;
}
sendrequest(request,
"admin="+arg+"&user="+user+"&passwd="+passwd+"&level="+level+"&URL="+url,
cantadd);
user = passwd = level = url = "";
}
function cancelAdd(){
user = passwd = level = url = "";
$("box").innerHTML = "";
$("box").style.display = "none";
}
function delUser(username){
var request;
function cantdel(req){
chkAns(req, "ÎÅ ÍÏÇÕ ÕÄÁÌÉÔØ ÐÏÌØÚÏ×ÁÔÅÌÑ<br>"+req.responseText);
}
sendrequest(request, "admin=userdel&user="+username, cantdel);
}
function editUser(username, ulevl, uurl){
user = username; passwd = "";
level = ulevl; url = uurl;
useradd("usermod");
}
</script>
<title>õÐÒÁ×ÌÅÎÉÅ ÐÏÌØÚÏ×ÁÔÅÌÑÍÉ</title>
</head>
<body onload="getcookie(CGI_PATH); run();">
<div class="shadow" id="box"></div>
<div id="inout" style="position: fixed; top: 10px; right: 10px; cursor: pointer; font-weight: bold;" onclick="inout();" >÷ÈÏÄ</div>
<div onclick="useradd('useradd');">îÏ×ÙÊ ÐÏÌØÚÏ×ÁÔÅÌØ</div>
<div id="userlist"></div>
<div id="msg" style="margin-top: 15px; color: red;"></div>
</body>
</html>

View File

@ -0,0 +1,96 @@
auth = function(){
var wsKey = "";
function _ilogin(){
$("inout").innerHTML = "Log in";
$("inout").onclick = auth.login;
}
function _ilogout(){
$("shadow").style.display = "none";
$("inout").innerHTML = "Log out";
$("inout").onclick = auth.logout;
}
function _wsk(request){
wsKey = request.responseText;
if(wsKey) console.log("Web key received: " + wsKey);
wsinit();
}
function reqAuth(request){
var txt = request.responseText;
dbg("received " + txt);
if(txt == "AuthOK"){ // cookies received
sendrequest("get/?getWSkey", _wsk);
_ilogout();
}else if(txt == "NeedAuth"){
_ilogin();
}else{
parseErr(txt);
}
}
function init1(){
sendrequest("auth/?check=1", reqAuth);
var l = document.createElement('a');
l.id = "inout";
l.href = "#";
var s1 = document.createElement('style');
s1.type = 'text/css';
s1.innerHTML = ".inout{position:absolute;top:0;right:0;background-color:green;"
document.body.appendChild(s1);
l.className = "inout";
document.body.appendChild(l);
var d = document.createElement('div');
d.id = "shadow";
var s = document.createElement('style');
s.type = 'text/css';
s.innerHTML = '.shadow{position:absolute;text-align:center;vertical-align:center;top:0;display:none;left:0;width:100%;height:100%;background-color:lightGrey;opacity:0.9;}';
document.body.appendChild(s);
d.innerHTML = "<div>Login:</div><div><input type=text id='login'></div><div>Password:</div><div><input type=password id='passwd'></div><button onclick='auth.send();'>OK</button>";
d.className = "shadow";
document.body.appendChild(d);
}
function login1(){
$("shadow").style.display = "block";
}
function logout1(){
sendrequest("auth/?LogOut=1", _ilogin);
}
function sendlogpass(){
$("shadow").style.display = "none";
var l = $("login").value, p = $("passwd").value;
if(!l || !p){
parseErr("give login and password");
return;
}
var str = "auth/?login=" + l + "&passwd=" + p;
sendrequest(str, reqAuth);
}
// websockets
var ws;
function wsinit(){
delete(ws);
ws = new WebSocket('wss://localhost:8081');
ws.onopen = function(){ws.send("Akey="+wsKey);}; // send key after init
ws.onclose = function(evt){
var text = "WebSocket closed: ";
if(evt.wasClean) text += "by remote side";
else text += "connection lost"
$('wsmsgs').innerHTML = text;
};
ws.onmessage = function(evt){
$('wsmsgs').innerHTML = evt.data;
}
ws.onerror = function(err){
parseErr("WebSocket error " + err.message);
}
}
function wssend(txt){
ws.send(txt);
}
return{
init: init1,
login: login1,
logout: logout1,
send: sendlogpass,
wssend: wssend,
wsinit: wsinit
};
}();

View File

@ -0,0 +1,16 @@
<html>
<head>
<title>Index</title>
<script src="/static/requests.js" type="text/javascript" language="javascript"></script>
<script src="/static/auth.js" type="text/javascript" language="javascript"></script>
</head>
<body onload="auth.init();">
<p>Text
<p>More text
<p>
<div id="wsmsgs"></div>
<div id="errmsg" style='background-color: red;'></div>
<p>
<input type="text" id="wssnd" onchange="auth.wssend($('wssnd').value);">
</body>
</html>

View File

@ -0,0 +1,92 @@
<html>
<head>
<title>á×ÔÏÒÉÚÁÃÉÑ</title>
<script>
const CGI_PATH = "https://ishtar.sao.ru/cgi-bin/auth";
var URL=new Array(), RTN=null, reqnmbr=0;
function $(id){ return document.getElementById(id);}
function getargs(){
var QS = window.location.search.substring(1);
var pars = QS.split("&");
var i=0, s;
while((s=pars[i++])){
if(s.indexOf('URL') == 0){
s = s.split('=');
URL[URL.length] = s[1];
}
else if(s.indexOf('RTN') == 0){
s = s.split('=');
RTN = s[1];
}
}
if(URL.length == 0){alert("ïÔÓÕÔÓÔ×ÕÅÔ URL ÄÌÑ Á×ÔÏÒÉÚÁÃÉÉ"); return;}
if(RTN == null) RTN = URL[0];
}
function sendrequest(req_STR, onOK){
var request = new XMLHttpRequest(), timeout_id;
request.open("POST", CGI_PATH, true);
request.setRequestHeader("Accept-Charset", "koi8-r");
request.overrideMimeType("multipart/form-data; charset=koi8-r");
request.onreadystatechange=function(){
if (request.readyState == 4){
if (request.status == 200){
clearTimeout(timeout_id);
if(onOK) onOK(request);
}
else{
clearTimeout(timeout_id);
parseErr("request sending error");
}
}
}
request.send(req_STR);
timeout_id = setTimeout(function(){request.onreadystatechange=null; request.abort(); parseErr("request timeout");}, 3000);
}
function subm(id){
var str, str1, i;
var login = $('login').value;
var pass = $('passwd').value;
if(login == "" || pass == ""){
if(id) $(id).focus();
return;
}
var str = "login=" + login + " passwd=" + pass;
for(i = 0; i < URL.length; i++){
str1 = str + " URL=" + URL[i];
sendrequest(str1);
}
}
function onOK(request){
var txt = request.responseText;
if(txt.indexOf("KEY") != 0){
parseErr(txt);
return;
}
var n = txt.indexOf('\n');
if(n) txt = txt.substring(0, n);
var d = new Date();
d.setTime(d.getTime() + 72e6); // ÓÒÏË ÄÅÊÓÔ×ÉÑ ËÕËÉ - 20 ÞÁÓÏ×
txt += "; expires="+d.toGMTString();
document.cookie = txt;
if(++reqnmbr == URL.length) document.location.href = RTN;
}
function parseErr(txt){
console.log("Error: " + txt);
$('msg').innerHTML = "Error: " + txt;
setTimeout(function(){$('msg').innerHTML = "";}, 3500);
}
</script>
</head>
<body onload="getargs();">
<div align="center">
<h2>ðÏÖÁÌÕÊÓÔÁ ××ÅÄÉÔÅ ÒÅÇÉÓÔÒÁÃÉÏÎÎÕÀ ÉÎÆÏÒÍÁÃÉÀ ÄÌÑ ÐÏÌÕÞÅÎÉÑ ÄÏÓÔÕÐÁ Ë ÓÅÒ×ÉÓÁÍ</h2>
<div style="border: 1px solid; text-align: center; width: 200px; margin: 0 auto; padding: 5px;" id="pass">
<div>éÍÑ:</div><div><input type=text id="login" onchange="subm('passwd');"></div>
<div>ðÁÒÏÌØ:</div><div><input type=password id="passwd" onchange="subm('login');"></div><br>
<div align=center><button onclick="subm();">OK</button></div>
</div>
<div id="msg" style="margin-top: 15px; color: red;"></div>
</div>
<body>
</html>

View File

@ -0,0 +1,56 @@
// move this file to the root html directory
// change const's EXURL & PASSURL
var KEY;
const PASSURL="https://ishtar.sao.ru/pass";
const EXURL = "https://ishtar.sao.ru/cgi-bin/auth";
function $(id){
return document.getElementById(id);
}
function checkcookie(){
var txt = document.cookie;
if(txt.length==0 || txt.indexOf('KEY')<0){
$("inout").innerHTML = "÷ÏÊÔÉ";
return 0;
}
else{
$("inout").innerHTML = "÷ÙÊÔÉ";
return 1;
}
}
function getcookie(){
/* ÂÅÚ ÁÒÇÕÍÅÎÔÏ× - ÄÌÑ ÔÅËÕÝÅÊ ÓÔÒÁÎÉÃÙ,
ËÁÖÄÙÊ ÁÒÇÕÍÅÎÔ - ÄÏÐ. "ÐÅÞÅÎØËÁ"
*/
var i, newurl = PASSURL+"?URL="+document.location.href;
for(i = 0; i < getcookie.arguments.length; i++)
newurl += "&URL=" + getcookie.arguments[i];
if(!checkcookie())
document.location.href = newurl;
}
function onEX(){
var d = new Date();
d.setTime(d.getTime() - 1000);
var str = "KEY=; expires="+d.toGMTString()+"; path="+document.location.pathname;
document.cookie = str;
window.location.reload();
}
function exit(){
var request = new XMLHttpRequest();
request.open("POST", EXURL, true);
request.setRequestHeader("Accept-Charset", "koi8-r");
request.setRequestHeader("Cookie", document.cookie);
request.overrideMimeType("multipart/form-data; charset=koi8-r");
request.onreadystatechange=function(){
if (request.readyState == 4){
if (request.status == 200){
onEX();
}
else alert("ïÛÉÂËÁ ÓÏÅÄÉÎÅÎÉÑ");
}
}
request.send("")
}
function inout(){
if(checkcookie()) exit();
else getcookie();
}

View File

@ -0,0 +1,43 @@
const Debug = true;
var elementsCache = {};
function $(id) {
if (elementsCache[id] === undefined)
elementsCache[id] = document.getElementById(id);
return elementsCache[id];
}
function dbg(text){
if(Debug) console.log("Debug message: " + text);
}
function sendrequest(req_STR, onOK, postdata){
var request = new XMLHttpRequest(), timeout_id;
dbg("send request " + req_STR);
var method = postdata ? "POST" : "GET";
request.open(method, req_STR, true);
//request.setRequestHeader("Accept-Charset", "koi8-r");
//request.overrideMimeType("multipart/form-data; charset=koi8-r");
request.onreadystatechange=function(){
if(request.readyState == 4){
if(request.status == 200){
clearTimeout(timeout_id);
if(onOK) onOK(request);
}
else{
clearTimeout(timeout_id);
parseErr("request sending error");
}
}
}
request.send(postdata);
timeout_id = setTimeout(function(){request.onreadystatechange=null; request.abort(); parseErr("request timeout");}, 5000);
}
function parseErr(txt){
console.log("Error: " + txt);
var msgDiv = $('errmsg');
if(!msgDiv) return;
msgDiv.innerHTML = "Error: " + txt;
setTimeout(function(){msgDiv.innerHTML = "";}, 3500);
}

88
NES_webiface/websockets.c Normal file
View File

@ -0,0 +1,88 @@
/*
* This file is part of the NES_web project.
* Copyright 2020 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 "auth.h"
#include "websockets.h"
#include <errno.h>
#include <onion/log.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#define BUFLEN 255
// bit-fields of `data` field (websocket_cont)
#define WS_FLAG_NOTAUTHORIZED 1
static onion_connection_status websocket_cont(void *data, onion_websocket *ws, ssize_t dlen){
FNAME();
uint32_t flags = *((uint32_t*)data);
char tmp[BUFLEN+1];
if(dlen > BUFLEN) dlen = BUFLEN;
int len = onion_websocket_read(ws, tmp, dlen);
if(len <= 0){
ONION_ERROR("Error reading data: %d: %s (%d)", errno, strerror(errno), dlen);
return OCS_NEED_MORE_DATA;
}
tmp[len] = 0;
//ONION_INFO("Read from websocket: %s (len=%d)", tmp, len);
DBG("WS: got %s", tmp);
if(flags & WS_FLAG_NOTAUTHORIZED){ // not authorized over websocket
sessinfo *session = NULL;
if(strncmp(tmp, "Akey=", 5) == 0){ // got authorized key - check it
char *key = tmp + 5;
session = getSession(key);
/* here we should make a proper check, but for now do simplest */
}
if(!session){
onion_websocket_printf(ws, AUTH_ANS_NEEDAUTH);
WARNX("Wrong websocket session ID");
return OCS_FORBIDDEN;
}
flags &= ~WS_FLAG_NOTAUTHORIZED; // clear non-authorized flag
return OCS_NEED_MORE_DATA;
}
char *eq = strchr(tmp, '=');
if(eq){
*eq++ = 0;
onion_websocket_printf(ws, "parameter: '%s', its value: '%s'", tmp, eq);
}else{
onion_websocket_printf(ws, "Echo: %s", tmp);
}
return OCS_NEED_MORE_DATA;
}
onion_connection_status websocket_run(_U_ void *data, onion_request *req, onion_response *res){
FNAME();
onion_websocket *ws = onion_websocket_new(req, res);
if (!ws){
green("PROC\n");
DBG("Processed");
return OCS_PROCESSED;
}
DBG("WS ready");
const char *host = onion_request_get_client_description(req);
const char *UA = onion_request_get_header(req, "User-Agent");
green("Got WS connection from %s (UA: %s)\n", host, UA);
uint32_t *flags = calloc(1, 4);
onion_websocket_set_userdata(ws, (void*)flags, free);
onion_websocket_set_callback(ws, websocket_cont);
return OCS_WEBSOCKET;
}

26
NES_webiface/websockets.h Normal file
View File

@ -0,0 +1,26 @@
/*
* This file is part of the NES_web project.
* Copyright 2020 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
#ifndef WEBSOCKETS_H__
#define WEBSOCKETS_H__
#include <onion/onion.h>
#include <onion/websocket.h>
onion_connection_status websocket_run(void *data, onion_request *req, onion_response *res);
#endif // WEBSOCKETS_H__

6
README
View File

@ -9,6 +9,8 @@ fifo_lifo - simple stack-like queues
netdaemon - snippet for network management of serial devices
serialsock - socket server for serial devices
simple_list - 1-directional list with functions: add element; delete list
stellarium_emul - snippet for stellarium telescope remote control
@ -23,3 +25,7 @@ usefull_macros - a lot of different macros & functions
* MMAP files into memory
USBrelay - simplest tool to manage USB-HID chinese relays
translate c into asm: gcc -S -fverbose-asm file.c

View File

@ -0,0 +1 @@
SUBSYSTEMS=="net", ACTION=="add", ENV{INTERFACE}=="can0", RUN+="/etc/udev/rules.d/socketcan0"

5
Socket_CAN/HOWTO.cert Normal file
View File

@ -0,0 +1,5 @@
create:
openssl req -newkey rsa:2048 -nodes -keyout cert.key -x509 -days 10100 -out cert.pem
review:
openssl x509 -text -noout -in cert.pem

63
Socket_CAN/Makefile Normal file
View File

@ -0,0 +1,63 @@
# run `make DEF=...` to add extra defines
CLIENT := soccanclient
SERVER := soccanserver
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 -Wno-trampolines -std=gnu99 -pthread
COMMSRCS := sslsock.c daemon.c cmdlnopts.c
SSRC := server.c can_io.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
#CXX = g++
all : $(SOBJDIR) $(COBJDIR) $(CLIENT) $(SERVER)
debug: DEFINES += -DEBUG -Werror -Wall -Wextra
debug: all
$(CLIENT) : DEFINES += -DCLIENT
$(CLIENT) : $(COBJS)
@echo -e "\t\tLD $(CLIENT)"
$(CC) $(LDFLAGS) $(COBJS) -o $(CLIENT)
$(SERVER) : DEFINES += -DSERVER
$(SERVER) : $(SOBJS)
@echo -e "\t\tLD $(SERVER)"
$(CC) $(LDFLAGS) $(SOBJS) -o $(SERVER)
$(SOBJDIR):
mkdir $(SOBJDIR)
$(COBJDIR):
mkdir $(COBJDIR)
ifneq ($(MAKECMDGOALS),clean)
-include $(SDEPS) $(CDEPS)
endif
$(SOBJDIR)/%.o: %.c
@echo -e "\t\tCC $<"
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
$(COBJDIR)/%.o: %.c
@echo -e "\t\tCC $<"
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
clean:
@echo -e "\t\tCLEAN"
@rm -f $(SOBJS) $(SDEPS) $(COBJS) $(CDEPS) 2>/dev/null || true
@rmdir $(SOBJDIR) $(COBJDIR) 2>/dev/null || true
xclean: clean
@rm -f $(PROGRAM)
.PHONY: clean xclean

62
Socket_CAN/Makefile.bk Normal file
View File

@ -0,0 +1,62 @@
# run `make DEF=...` to add extra defines
CLIENT := soccanclient
SERVER := soccanserver
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
LDFLAGS += -lusefull_macros
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
OBJDIR := mk
CFLAGS += -O2 -Wno-trampolines -std=gnu99
SSRC := server.c
CSRC := client.c
COMMSRCS := sslsock.c daemon.c cmdlnopts.c
SOBJS := $(addprefix $(OBJDIR)/, $(SSRC:%.c=%.o))
COBJS := $(addprefix $(OBJDIR)/, $(CSRC:%.c=%.o))
COMMOBJS := $(addprefix $(OBJDIR)/, $(COMMSRCS:%.c=%.o))
OBJS := $(SOBJS) $(COBJS) $(COMMOBJS)
DEPS := $(OBJS:.o=.d)
CC = gcc
#CXX = g++
all : $(OBJDIR) $(CLIENT) $(SERVER)
debug: DEFINES += -DEBUG -Werror -Wall -Wextra
debug: all
$(CLIENT) : DEFINES += -DCLIENT
$(CLIENT) : $(COBJS)
@rm $(COMMOBJS) 2>/dev/null || true
@make DEF="$(DEFINES)" $(COMMOBJS)
@echo -e "\t\tLD $(CLIENT)"
$(CC) $(LDFLAGS) $(COBJS) $(COMMOBJS) -o $(CLIENT)
$(SERVER) : DEFINES += -DSERVER
$(SERVER) : $(SOBJS)
@rm $(COMMOBJS) 2>/dev/null || true
@make DEF="$(DEFINES)" $(COMMOBJS)
@echo -e "\t\tLD $(SERVER)"
$(CC) $(LDFLAGS) $(SOBJS) $(COMMOBJS) -o $(SERVER)
@rm $(COMMOBJS)
$(OBJDIR):
mkdir $(OBJDIR)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR)/%.o: %.c
@echo -e "\t\tCC $<"
$(CC) -MD -c $(LDFLAGS) $(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

7
Socket_CAN/README_ports Normal file
View File

@ -0,0 +1,7 @@
server:
socat pty,link=/tmp/ttyX0,waitslave tcp:127.0.0.1:2002 &
ssh -L 2002:robotel1:2000 -N -f robotel1 &
client:
socat TCP-LISTEN:2000 PTY,link=/tmp/ttyX0,raw,crnl &

15
Socket_CAN/Readme.md Normal file
View File

@ -0,0 +1,15 @@
CANbus messages retranslation from BTA bus into Dome bus
RUN server:
./soccanserver -i localhost -l log.log -a ca/ca/ca_cert.pem -c ca/server/server_cert.pem -k ca/server/private/server_key.pem
RUN client:
./soccanclient -i localhost -a ca/ca/ca_cert.pem -c ca/client/client_cert.pem -k ca/client/private/client_key.pem
Compile i686:
export LDFLAGS=-m32

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,6 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42
#define EBUG
#define SERVER
#define CLIENT
#define _GNU_SOURCE

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.15.1, 2021-12-15T14:17:48. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">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="int" 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="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</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">2</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</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">true</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<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">4</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">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/eddy/Docs/SAO/ELECTRONICS/CAN_controller/Socket_CAN</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="int" 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="int" 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"/>
<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="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" 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="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">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>

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,16 @@
Makefile
can4linux.h
can_io.c
can_io.h
canbus.h
cansock.c
client.c
client.h
cmdlnopts.c
cmdlnopts.h
daemon.c
daemon.h
server.c
server.h
sslsock.c
sslsock.h

View File

@ -0,0 +1 @@
.

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIUV/DCy65P7x+mJpXLG5/WIqplrN0wDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNVBAcMBkJ1a292
bzEQMA4GA1UECgwHU0FPIFJBUzEPMA0GA1UEAwwGc2FvLnJ1MCAXDTIxMTIyMDA4
MTczOVoYDzIxMjExMjIwMDgxNzM5WjBQMQswCQYDVQQGEwJSVTENMAsGA1UECAwE
S0NoUjEPMA0GA1UEBwwGQnVrb3ZvMRAwDgYDVQQKDAdTQU8gUkFTMQ8wDQYDVQQD
DAZzYW8ucnUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDj+eCtYWLL
GHXbkv3yidLuNEL9CYoJjcEYs1lPKSGypYHUHTQRu+12bRinDCkFBlL9LxJl7PeG
/wek1DXUG6OvOKdB6B4R227YJOUfKSSq15yu5Zwumimki8Z7xCXJdsCWPxoo12aC
u6DUdofaoO/nWhcEnZV98l4H4vZUBCj8wVah5DwLOdvom1duw6oJr6kG7zKFjnpI
0OCbRSUOULJId8hVyXXpUb9TYjPjo/6Btp8XNbnAJ4Q5hk/PYqHidroKJQydFeHI
YC7QPA9EtLNMtCfIlhjjhaAYKTgU3s18BcNuVP90ZJat9lu08qUYHsT6/GoxcRUp
SGQ+Bh4oikBinPUDMUBMlMcWlU7LLSS130mZrFxVdbKIG0sDgsfk/ivjvEFHx52g
CAmp26wzG3u+r0aVFLbyqGwlNgoiggMYfqyJrrhgFvtumuExqts9CtCdbKWlfWx0
9g27+UuRgWC/+KFU+MVdzd6Ezq+518r3Sx9C20ANS7r+XmFaYkYkv6pCfkMUHOMX
hUvyGpkmqq67zm6MHQi0A/p68sQjcKvSah1zVe/VhNNqkK3GNoiYfH0VKLtad+TJ
tyH+sCe06pGr7sfXxN8AfrHnHrM8mqeERpnwRqmHoZC5let/vM+wKYh2+n0uJ/nT
lJZFRiESAUwfD4orijqgz8tMSOR6nWBKlwIDAQABo1MwUTAdBgNVHQ4EFgQUhe1A
CZbaeGI06X/DOLvvpnFlujwwHwYDVR0jBBgwFoAUhe1ACZbaeGI06X/DOLvvpnFl
ujwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAPDZVdlVYA2zo
j0ZmILvf8LvSWMst7FXVSb+9RMHTwTJmxoN6veIuWoaqv95bN+08At30Ri6U+/p/
C4YK+JNU/eTKrr0PPt0+Y/eZJbrk2uU1rkCzlJXZacocZpWHYH6y//dRI7ygJigv
mI4ksyQl+Csu4rjYS7BozLJj8r0GwatJaCjq/CAmw1khU/cKWHKFEdircE5puJ5s
dvqc41UoIhS+XGk4ld5gbbk8CjcqEymR3vzYiyCHJK2GG7/mSx9ULpOLXAbfQox4
IRIfKvkl+/mwqez9pLO6wbY7B8+pexmRzC1gn8ZN8zPgmkJTq/0nQG4wFlkerGOu
Un/E+sEcx++nT38mc77rKsyMQez28PKB1yjkdERYzGFmZtbw2yhHkFZ7z/VCHdDq
IoJTOn9nVKimc2JId3rMSvE9Sfga8+GCmzeJz1MaeR+E5g00B0FlF0KVvFsldsdq
Y221sfKA7aZyiPzad2LGLBGno+inZHykvN+KKmZLA+Qz7D/TQzix5XoBbkCQeVua
5MZdQv7zft1YmbMNr2CFnI5Clibcet/cN5UNlQqBWj76gf6H76eWzMPTiJa5Ago1
YxGsfSBVdfrzoP48heVEHs8OD4IlbHYtaMLTVFjPNDDDoorOU+ei1vLfyIGMFDFG
jfORPJGT7YvQ0nFHf9V3Bw2ynuaSUzU=
-----END CERTIFICATE-----

View File

@ -0,0 +1 @@
1D339878EE448B717AB9855CC9DFBBC66C06FF8D

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
XiPSjQhMjqLaniianqcVkhCKCicsk0c=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEhDCCAmwCAQAwPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNV
BAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBALwxkEemASoXwJu8qEIFhnOqrn50Gj6xI8O4/2RyyB35nisR
3VB24a5cdBvXzCq7PfuVzCtHCnKbLxPJhA5HJAB4MHOyXH97NKp0Ihpp/8A3FWJq
/oNF0QpnBlSKVlQ/5Omh7kWzzqNlQHIIJpgyZZJlePi1RBjCK0h+HZMg+/1jutvt
FRGXOcbRLke8Vm5NSmdPBAYLCzaoByCCqefhxmdiSnUC6TgPqiGgSxQ7SSMB/sdb
ADd9e8pEZRGWMyU2z6X8GtuyRkIKC4txdPYdAxBNSe32nHDOx4RE0GsjSzqCQ2di
+mzh4mbeQGJLQprqNRLbR1moppumBM9qatL9bDr+NIOJVj9y2RqmEbuXLzV8f0vM
v61k89ZlQJro5y+4AffwpKXDrHqXaALhUvD80xZDvTRRZrBTC5cLvCC/Qx5Lq8hI
/Yp/N8E4ZLIJGL0PJ0gKiuUQabLGNydqLxLuDp4nwPvRQdsB/MVErlw1ojA9L0wN
WvxTJl5tCflcIH9imCceuIhGP9+TGuJdknZPk1pl97wfQjWGunbkHWzw20bQXL1f
sqZOYHIaRjX/TRsIo8A6xOo9tM+J8tlkr6Vbf177xv+yxblMOhMnbi77efSKDImj
mqrAm7EzxzOximrAxRjJEANtgfoFCY205cwM8uepnJYoTg4ZY4YLuHX9lInvAgMB
AAGgADANBgkqhkiG9w0BAQsFAAOCAgEAsiA2FfbdcQCFlYhgD2vZZMqOvJD3IfT+
2Jt1QTfLBiPtqCIa20A6lHCaRQ8FxQ3IbpJpSlpJ9Abj1UqOkNFHKCHIfIbkITr9
uG/UlWu3dQ0FnmxpM5i6Vm4yEgZymGbNDObmCP50Ai1mtGpFjb3lYIv8sgX/e9+B
d/evTvzH+QjSWGzABLe7uMv94wkjWm+LFSVT9XIQ8FzkoIz17QQTcg8q5ku5qveW
SYmOS3rOxWzh+kYJG8+qkLw9ga6T+/lydzqb6gr7iW2pEUSFCWpwb9v7yFk8uZ6L
NZe3YW6e601uqzPpvH0/aQSIbnh//vVTHxFitA+lCENYgIaz8/TAT3/VQhCiM3OZ
cTEZWIPRtlV84c9kIvxfYTthmS3vnFPSMvI5STUmFA1MSvuxOx1c5BUtdlMv/58x
WL8btQB1oRqt6KuMKnwsUA8nv84MRzkH0tCatmcuAIy2/NXrsbo/F794jwCFJFWp
nEoKFnCIhpFvAYTV70BV0jkAQ3iLV4RpU6LdqdVtzDQbjbFd/bWIjT/A7/xEoMI7
jaJfPE/1JaDnzFD20WTSXU1SrOcFlDRURrfYRrWMxaqCHBQbp+OwTaa3ut928tZk
Gs/m+Ov6hMNt54tbBmc2Yc42rbBStBbUKjO4YpwMbFm/dyjFZ4dGx/K54XMve7R6
dFl5goynSwo=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFGDCCAwACFB0zmHjuRItxermFXMnfu8ZsBv+NMA0GCSqGSIb3DQEBCwUAMFAx
CzAJBgNVBAYTAlJVMQ0wCwYDVQQIDARLQ2hSMQ8wDQYDVQQHDAZCdWtvdm8xEDAO
BgNVBAoMB1NBTyBSQVMxDzANBgNVBAMMBnNhby5ydTAgFw0yMTEyMjAwODE3NDBa
GA8yMTIxMTIyMDA4MTc0MFowPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIx
DzANBgNVBAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcN
AQEBBQADggIPADCCAgoCggIBALwxkEemASoXwJu8qEIFhnOqrn50Gj6xI8O4/2Ry
yB35nisR3VB24a5cdBvXzCq7PfuVzCtHCnKbLxPJhA5HJAB4MHOyXH97NKp0Ihpp
/8A3FWJq/oNF0QpnBlSKVlQ/5Omh7kWzzqNlQHIIJpgyZZJlePi1RBjCK0h+HZMg
+/1jutvtFRGXOcbRLke8Vm5NSmdPBAYLCzaoByCCqefhxmdiSnUC6TgPqiGgSxQ7
SSMB/sdbADd9e8pEZRGWMyU2z6X8GtuyRkIKC4txdPYdAxBNSe32nHDOx4RE0Gsj
SzqCQ2di+mzh4mbeQGJLQprqNRLbR1moppumBM9qatL9bDr+NIOJVj9y2RqmEbuX
LzV8f0vMv61k89ZlQJro5y+4AffwpKXDrHqXaALhUvD80xZDvTRRZrBTC5cLvCC/
Qx5Lq8hI/Yp/N8E4ZLIJGL0PJ0gKiuUQabLGNydqLxLuDp4nwPvRQdsB/MVErlw1
ojA9L0wNWvxTJl5tCflcIH9imCceuIhGP9+TGuJdknZPk1pl97wfQjWGunbkHWzw
20bQXL1fsqZOYHIaRjX/TRsIo8A6xOo9tM+J8tlkr6Vbf177xv+yxblMOhMnbi77
efSKDImjmqrAm7EzxzOximrAxRjJEANtgfoFCY205cwM8uepnJYoTg4ZY4YLuHX9
lInvAgMBAAEwDQYJKoZIhvcNAQELBQADggIBALG6oAI97WWjGKVkfZZMDCztpkJw
GQOPArtPqz+rmVPC2YsqAK891TbiJ+vfniQ4u17+oJp+UeeTfBkRsLyHpdIWjqpZ
OG5Q3+fIMvlWKv1Vds76gY6fbnGPdI3rFZASl4qhN85K3a8T7AsB4pkBjJg0W/sO
sgDU3s+8XUsuSQtIhgXS4Rl2upEjrJ1/9/3HOwcsHX7Qe2VgInN8r2vLXz0Tn86A
yB0ZqmhqjTaA6aObd3ncx2m4UD9IU2h1IoDRpmJP9Rfeyovdp1IGS5FQEsrJkxXS
gYCgzsWq1X6u3NF9Rtu4b8tY7BGK11lvds2J8QCJZomW6A+WSNK20ksR044SlSy6
RvNioaDIkL0mKhMu87DJnB2phDTmF/6SFVQtNt9MWtOuAUujrn9FiNVeBxqnkzP7
Fq4zMgmZVjKydA5BiR1cXCH/0C+jOPmaGUSvxVLX9B3U81Tj2jBBLh5C4QHy/D8f
DPFN5WOrpbrfH9HOstC0Kk4bLHq4xsFtwEz7N/8aL9ahutejVlaKaviaBA3sJ+uv
oz0cEa8Bn4owDDnGwRPeimxNHd+C8dulUoZQ/ukT8ah6cuo35ykYdOiOgqewTsPT
2J3hJa1DqD9zkH2BzLatHEdZkXpNtgF8RRpDH3m1ZsS4a4ykd03oYEoh3XOFVbwi
myCZgwzp8pk0xfV1
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

49
Socket_CAN/ca/gen.sh Executable file
View 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.

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEhDCCAmwCAQAwPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNV
BAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAM/Iqcldh+6UfwTJySrY7WpzFlUTq2c0nwTyvRDDP1+R9Ryt
zyOwnkKMRvU4Bs40iftpacwlU4JfQGGeQn1g7507rRlalIxt11D2kB+hHh7V8pS1
3Y5Y1yOTZlhExdKrs1A3EpaEl4wurF2Rxk6fQMfGoi18Tx/jvjy5diT2LTWv+KOb
xFIJv7o0ikLdvdLFdqobWn4xaYNjP6M7+3IlWCU8N0ickAprKBHOf9jFgE3ABjNu
MYUY1gX9dJbkEbhjrvIcbjC5MmknMoR1HHbG8MHLgEbcbu0ZFgj7pu01QfiByEbL
gDT1xl4pZ8S+6d4q7lPmkwmJzwGJr5ZmCqyT7A4i01AqVRHbezZvR0oV4QusErhK
xvGh7123H/OrSvfwIxEIbWEAIzvWaxV14ctLKOiOw+mENltQxFWziaiGNHXM38SH
QNSmTHJUWC08IUqslR5r1Ys4h15vdJ+ZMVpw1ijNIW0C9zgdJu/ERkaCnVSW5rl3
gicou4NobomKscHfnyYTiPUWZ+GccnNR35xlSbGSmjzPwYtvvX0UOn8wSp2oCeW6
PQuWJDpVOlTFgQwJNVT6T9J2XW/wtlNWyZGjM3tATqAlpXTrrapJzTlLuEGpkebK
IMTLnDa/DXemy1A7ehjyKMXfXC4pzwEsR12oPfZ1qyO+lQJPYV0Ur+onSNlZAgMB
AAGgADANBgkqhkiG9w0BAQsFAAOCAgEAAD2CuiczzQnU0+W2lYGuxueCbIkD6ZW0
li48K4iu5kp2Axv4H/d9o61nHn8E+fqkZLNB10eIut/45yRXUA6cWjBTtMZR1Gcz
BFXnaau1agCnFEqqanU7uBJpflQD+soieICjtQGyrqCTSJlaZy9V/iv5lYCvCn8T
mP9yiUkoAKOnAf/XHLkyBrt7IW2ovT5raMcBj0z/5GNNKouhu2kdWEk/BH9zMvYY
uccf2+YMbtdHDUG+0qlSVAYOyDONbJgHAFfROVdlfN2mVDMkUSnSg9/IE4aevP9s
LuHBzCJzodRqpMuIgV+GtTETzWcZvnaXKLmOvKCnLQtxP2OZYrF4EwxK2SdDPCW+
YQYu51YF/tBumZbCWrGThg5YYGJfghTaINr8x51j1lQNBTGh1gi3I5633eGtIlpW
HUaY49KSQ+yBefmHMWesC6dbSbHfqvfbugidVAk8eFNS3weo26AJsv3dPNKani/+
c0bOeEJ4tfIQUEpTRlPC5MWqEk0/wz9XC1hPAYHV4YZExQ+kBQ2xyEa0w/9yLuWG
5sZjd/k/XBnz/V6rrx+jPeqikaaScCGR8YpCwglWEmn6K1Mp6ciSNAFqGTeVnhwD
U0E3INACyi984vDzL886zWt7J1zT0t6IpIaDnxDpmoqvJDK5+xVaw4059HeXwaYb
5zpySGMozeg=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFGDCCAwACFB0zmHjuRItxermFXMnfu8ZsBv+MMA0GCSqGSIb3DQEBCwUAMFAx
CzAJBgNVBAYTAlJVMQ0wCwYDVQQIDARLQ2hSMQ8wDQYDVQQHDAZCdWtvdm8xEDAO
BgNVBAoMB1NBTyBSQVMxDzANBgNVBAMMBnNhby5ydTAgFw0yMTEyMjAwODE3NDBa
GA8yMTIxMTIyMDA4MTc0MFowPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIx
DzANBgNVBAcMBkJ1a292bzEQMA4GA1UECgwHU0FPIFJBUzCCAiIwDQYJKoZIhvcN
AQEBBQADggIPADCCAgoCggIBAM/Iqcldh+6UfwTJySrY7WpzFlUTq2c0nwTyvRDD
P1+R9RytzyOwnkKMRvU4Bs40iftpacwlU4JfQGGeQn1g7507rRlalIxt11D2kB+h
Hh7V8pS13Y5Y1yOTZlhExdKrs1A3EpaEl4wurF2Rxk6fQMfGoi18Tx/jvjy5diT2
LTWv+KObxFIJv7o0ikLdvdLFdqobWn4xaYNjP6M7+3IlWCU8N0ickAprKBHOf9jF
gE3ABjNuMYUY1gX9dJbkEbhjrvIcbjC5MmknMoR1HHbG8MHLgEbcbu0ZFgj7pu01
QfiByEbLgDT1xl4pZ8S+6d4q7lPmkwmJzwGJr5ZmCqyT7A4i01AqVRHbezZvR0oV
4QusErhKxvGh7123H/OrSvfwIxEIbWEAIzvWaxV14ctLKOiOw+mENltQxFWziaiG
NHXM38SHQNSmTHJUWC08IUqslR5r1Ys4h15vdJ+ZMVpw1ijNIW0C9zgdJu/ERkaC
nVSW5rl3gicou4NobomKscHfnyYTiPUWZ+GccnNR35xlSbGSmjzPwYtvvX0UOn8w
Sp2oCeW6PQuWJDpVOlTFgQwJNVT6T9J2XW/wtlNWyZGjM3tATqAlpXTrrapJzTlL
uEGpkebKIMTLnDa/DXemy1A7ehjyKMXfXC4pzwEsR12oPfZ1qyO+lQJPYV0Ur+on
SNlZAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAJH7lEF/tpLN0uOz7pnUdTT2A3IF
0cehqlhv2E8pXLQ7lydAwMuSh582/jCPx9f/aAz11MKfcUwdSBJvdinLvOpM0qCN
0i2aCxHGXnC9kVRsukCkKbF2/+hqoM/Cfmi995f1eEgrk/QkR34HC/2745mh//3S
i69jzwbEGDM7YuGisY25OYSyT37SHR56dU/j1SRw9JgfIEC4DPzS38oqWQfEJjEk
VCg2XrPwUmRk6eLqC80pBL4tmItT1RTERN/0vBNVivHZrseEGWrtMsq52mDEPuVf
mijF6ImVkonKi7ouIrTE/4no3TpNjjUTlMNYumedO38q30/4A0+wjHvsP42b9e5V
bahjejgwlhhbFaEkUpKk9ApcM6Pqq8kURoWgzcmjrUYuXryWAPKWTwE5Y939CgsV
DJXbSkITl61jZQWyAzl5kEUPVguBwJjOyL2zsxbLcHuvIx51RDEa0xl9sKp4kCJR
FoLyexymVyMw8yPenPL0jF7LvY1ua6+60avE7tNnJ0DdWt0DIjEUtqmuQEfBUP0M
n0q/fO9+ZOqfOhuNLPKlw2UFKCw2xd6SU6qm0BFebLiIBkvU4GQ3IWwNGZWw+66J
UzDRHzWiVNED+IBrdGE43+1TdROkKz0HCiIrAnE7gjWeaq2ShGmjaabyYOFE0FoJ
Qx01GFwRqiRjVnId
-----END CERTIFICATE-----

211
Socket_CAN/can4linux.h Normal file
View File

@ -0,0 +1,211 @@
/*
* can4linux.h - can4linux CAN driver module
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 2001 port GmbH Halle/Saale
*------------------------------------------------------------------
* $Header: /z2/cvsroot/products/0530/software/can4linux/src/can4linux.h,v 1.5 2004/05/14 10:02:54 oe Exp $
*
*--------------------------------------------------------------------------
*
*
* modification history
* --------------------
* $Log: can4linux.h,v $
* Revision 1.5 2004/05/14 10:02:54 oe
* - started supporting CPC-Card
* - version number in can4linux.h available
* - only one structure type for Config_par_t Command_par_t
* - new ioctl command CMD_CLEARBUFFERS
*
* Revision 1.4 2003/08/27 17:49:04 oe
* - New CanStatusPar structure
*
* Revision 1.3 2002/08/20 05:57:22 oe
* - new write() handling, now not ovrwriting buffer content if buffer fill
* - ioctl() get status returns buffer information
*
* Revision 1.2 2002/08/08 17:50:46 oe
* - MSG_ERR_MASK extended
*
* Revision 1.1 2002/01/10 19:13:19 oe
* - application header file changed name can.h -> can4linux.h
*
* Revision 1.2 2001/09/14 14:58:09 oe
* first free release
*
* Revision 1.1.1.1 2001/06/11 18:30:54 oe
* minimal version can4linux embedded, compile time Konfigurierbar
*
*
*
*
*--------------------------------------------------------------------------
*/
/**
* \file can.h
* \author Heinz-Jürgen Oertel, port GmbH
* $Revision: 1.5 $
* $Date: 2004/05/14 10:02:54 $
*
* can4linux interface definitions
*
*
*
*/
#ifndef __CAN_H
#define __CAN_H
#define CAN4LINUXVERSION 0x0301 /*(Version 3.1)*/
#ifndef __KERNEL__
#include <sys/time.h>
#endif
/*---------- the can message structure */
#define CAN_MSG_LENGTH 8 /**< maximum length of a CAN frame */
#define MSG_RTR (1<<0) /**< RTR Message */
#define MSG_OVR (1<<1) /**< CAN controller Msg overflow error */
#define MSG_EXT (1<<2) /**< extended message format */
#define MSG_PASSIVE (1<<4) /**< controller in error passive */
#define MSG_BUSOFF (1<<5) /**< controller Bus Off */
#define MSG_ (1<<6) /**< */
#define MSG_BOVR (1<<7) /**< receive/transmit buffer overflow */
/**
* mask used for detecting CAN errors in the canmsg_t flags field
*/
#define MSG_ERR_MASK (MSG_OVR + MSG_PASSIVE + MSG_BUSOFF + MSG_BOVR)
/**
* The CAN message structure.
* Used for all data transfers between the application and the driver
* using read() or write().
*/
typedef struct {
/** flags, indicating or controlling special message properties */
int flags;
int cob; /**< CAN object number, used in Full CAN */
unsigned long id; /**< CAN message ID, 4 bytes */
struct timeval timestamp; /**< time stamp for received messages */
short int length; /**< number of bytes in the CAN message */
unsigned char data[CAN_MSG_LENGTH]; /**< data, 0...8 bytes */
} canmsg_t;
/**
---------- IOCTL requests */
#define COMMAND 0 /**< IOCTL command request */
#define CONFIG 1 /**< IOCTL configuration request */
#define SEND 2 /**< IOCTL request */
#define RECEIVE 3 /**< IOCTL request */
#define CONFIGURERTR 4 /**< IOCTL request */
#define STATUS 5 /**< IOCTL status request */
/*---------- CAN ioctl parameter types */
/**
IOCTL Command request parameter structure */
struct Command_par {
int cmd; /**< special driver command */
int target; /**< special configuration target */
unsigned long val1; /**< 1. parameter for the target */
unsigned long val2; /**< 2. parameter for the target */
int error; /**< return value */
unsigned long retval; /**< return value */
};
typedef struct Command_par Command_par_t ;
/**
PSW made them all the same
IOCTL Configuration request parameter structure */
typedef struct Command_par Config_par_t ;
/**
IOCTL generic CAN controller status request parameter structure */
typedef struct CanStatusPar {
unsigned int baud; /**< actual bit rate */
unsigned int status; /**< CAN controller status register */
unsigned int error_warning_limit; /**< the error warning limit */
unsigned int rx_errors; /**< content of RX error counter */
unsigned int tx_errors; /**< content of TX error counter */
unsigned int error_code; /**< content of error code register */
unsigned int rx_buffer_size; /**< size of rx buffer */
unsigned int rx_buffer_used; /**< number of messages */
unsigned int tx_buffer_size; /**< size of tx buffer */
unsigned int tx_buffer_used; /**< number of messages */
unsigned long retval; /**< return value */
unsigned int type; /**< CAN controller / driver type */
} CanStatusPar_t;
/**
IOCTL CanStatusPar.type CAN controller hardware chips */
#define CAN_TYPE_UNSPEC 0
#define CAN_TYPE_SJA1000 1
#define CAN_TYPE_FlexCAN 2
#define CAN_TYPE_TouCAN 3
#define CAN_TYPE_82527 4
#define CAN_TYPE_TwinCAN 5
/**
IOCTL Send request parameter structure */
typedef struct Send_par {
canmsg_t *Tx; /**< CAN message struct */
int error; /**< return value for errno */
unsigned long retval; /**< return value */
} Send_par_t ;
/**
IOCTL Receive request parameter structure */
typedef struct Receive_par {
canmsg_t *Rx; /**< CAN message struct */
int error; /**< return value for errno */
unsigned long retval; /**< return value */
} Receive_par_t ;
/**
IOCTL ConfigureRTR request parameter structure */
typedef struct ConfigureRTR_par {
unsigned message; /**< CAN message ID */
canmsg_t *Tx; /**< CAN message struct */
int error; /**< return value for errno */
unsigned long retval; /**< return value */
} ConfigureRTR_par_t ;
/**
---------- IOCTL Command subcommands and there targets */
# define CMD_START 1
# define CMD_STOP 2
# define CMD_RESET 3
# define CMD_CLEARBUFFERS 4
/**
---------- IOCTL Configure targets */
# define CONF_ACC 0 /* mask and code */
# define CONF_ACCM 1 /* mask only */
# define CONF_ACCC 2 /* code only */
# define CONF_TIMING 3 /* bit timing */
# define CONF_OMODE 4 /* output control register */
# define CONF_FILTER 5
# define CONF_FENABLE 6
# define CONF_FDISABLE 7
#endif /* __CAN_H */

545
Socket_CAN/can_io.c Normal file
View File

@ -0,0 +1,545 @@
/* CAN I/O library (to use as a process)
* usage:
* first: fork() + start_can_io(NULL) - start CAN Rx-buffering process
* then: fork() + Control_1(....) - start process that uses recv/send functions
* ...........................
* then: fork() + Control_N(....)
*
* note: use init_can_io() at the begining of every Control process
* BUT DON't USE it in main() before Control process start
* ^^^^^^^^^^^^^
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/shm.h>
#include "can4linux.h"
#include "can_io.h"
char can_dev[40] = "/dev/can0";
int can_fd=-1;
char can_lck[40] = "/tmp/dev_can0.lock";
int can_lk=-1;
static int server_mode=0;
static int my_uid;
#define CAN_SHM_SIZE ((sizeof(int)*3)+CAN_CTLR_SIZE+(CAN_RX_SIZE*sizeof(canmsg_t)))
union ShMkey {
char name[5];
key_t code;
} can_shm_key;
int can_shm_id=-1;
char *can_shm_addr = NULL;
#define can_pid (*(((int *)can_shm_addr)+0)) /* PID of CAN I/O process */
#define can_open (*(((int *)can_shm_addr)+1)) /* file descr.of CAN-driver */
#define rx_buff_pntr (*(((int *)can_shm_addr)+2)) /* from 0 till CAN_RX_SIZE-1 */
void *can_ctrl_addr = NULL; /* shm area reserved for control process purpose*/
canmsg_t *rx_buff; /* rx ring buffer: CAN_RX_SIZE*sizeof(canmsg_t)*/
struct CMD_Queue { /* ÏÐÉÓÁÎÉÅ ÏÞÅÒÅÄÉ (ËÁÎÁÌÁ) ËÏÍÁÎÄ */
union {
char name[5]; /* ËÌÀÞ ÉÄÅÎÔÅÆÉËÁÃÉÉ ÏÞÅÒÅÄÉ */
key_t code;
} key;
int mode; /* ÒÅÖÉÍ ÄÏÓÔÕÐÁ (rwxrwxrwx) */
int side; /* ÔÉÐ ÐÏÄÓÏÅÄÉÎÅÎÉÑ: ëÌÉÅÎÔ/óÅÒ×ÅÒ (Sender/Receiver)*/
int id; /* ÄÅÓËÒÉÐÔÏÒ ÐÏÄÓÏÅÄÉÎÅÎÉÑ */
unsigned int acckey; /* ËÌÀÞ ÄÏÓÔÕÐÁ (ÄÌÑ ÐÅÒÅÄÁÞÉ ëÌÉÅÎÔ->óÅÒ×ÅÒ) */
};
/* ËÁÎÁÌ ËÏÍÁÎÄ ÉÓÐÏÌØÚÕÅÍ ÄÌÑ ÐÅÒÅÄÁÞÉ CAN-ÆÒÅÊÍÏ× */
static struct CMD_Queue canout = {{{'C','A','N',0,0}},0200,0,-1,0};
/* ÓÔÒÕËÔÕÒÁ ÓÏÏÂÝÅÎÉÑ */
struct my_msgbuf {
long mtype; /* type of message */
unsigned long acckey; /* ËÌÀÞ ÄÏÓÔÕÐÁ ËÌÉÅÎÔÁ */
unsigned long src_pid; /* ÎÏÍÅÒ ÐÒÏÃÅÓÓÁ ÉÓÔÏÞÎÉËÁ */
unsigned long src_ip; /* IP-ÁÄÒ. ÉÓÔÏÞÎÉËÁ, =0 - ÌÏËÁÌØÎÁÑ ËÏÍÁÎÄÁ */
char mtext[100]; /* message text */
};
static void can_abort(int sig);
void set_server_mode(int mode) {server_mode=mode;}
int can_server() {return(server_mode);}
int can_card() {return(can_fd>0);}
int can_gate() {return(0);}
double can_gate_time_offset() {return(0.0);}
unsigned long get_acckey() {return(0);}
static int shm_created=0;
/* to use _AFTER_ process forking */
void *init_can_io() { /* returns shared area addr. for client control process*/
int i;
int new_shm=0;
char *p, msg[100];
my_uid=geteuid();
if(can_shm_addr==NULL) {
if((p=strrchr(can_dev,'/'))!=NULL) {
memcpy(&can_lck[9], p+1, 4);
memcpy(can_shm_key.name, p+1, 4);
can_shm_key.name[4]='\0';
} else {
fprintf(stderr,"Wrong CAN device name: %s\n", can_dev);
return NULL;
}
can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, 0644);
if(can_shm_id<0 && errno==EACCES)
can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, 0444);
if(can_shm_id<0 && errno==ENOENT && server_mode) {
can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, IPC_CREAT|IPC_EXCL|0644);
new_shm = shm_created = 1;
}
if(can_shm_id<0) {
can_prtime(stderr);
if(new_shm)
sprintf(msg,"Can't create shm CAN buffer '%s'",can_shm_key.name);
else if(server_mode)
sprintf(msg,"CAN-I/O: Can't find shm segment for CAN buffer '%s'",can_shm_key.name);
else
sprintf(msg,"Can't find shm segment for CAN buffer '%s' (maybe no CAN-I/O process?)",can_shm_key.name);
perror(msg);
return NULL;
}
can_shm_addr = shmat(can_shm_id, NULL, 0);
if (can_shm_addr == (void*)-1 && errno==EACCES)
can_shm_addr = shmat(can_shm_id, NULL, SHM_RDONLY);
if (can_shm_addr == (void*)-1) {
sprintf(msg,"Can't attach shm CAN buffer '%s'",can_shm_key.name);
perror(msg);
shmctl(can_shm_id, IPC_RMID, NULL);
return NULL;
}
}
can_ctrl_addr = (canmsg_t *)(can_shm_addr+sizeof(int)*3);
rx_buff = (canmsg_t *)(can_ctrl_addr+CAN_CTLR_SIZE);
if(can_fd<0 && canout.id<0) {
if(server_mode) {
if(( can_fd = open(can_dev, O_RDWR )) < 0 ) {
sprintf(msg,"CAN-I/O: Error opening CAN device %s", can_dev);
can_prtime(stderr);
perror(msg);
shmctl(can_shm_id, IPC_RMID, NULL);
return NULL;
}
} else {
if((canout.id = msgget(canout.key.code, canout.mode)) < 0) {
sprintf(msg,"Error opening CAN output queue '%s'(maybe no CANqueue server process?) ",canout.key.name);
perror(msg);
}
}
}
if(can_lk>0) close(can_lk);
if(( can_lk = open(can_lck, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH )) < 0 ) {
sprintf(msg,"Error opening CAN device lock-file %s", can_lck);
perror(msg);
shmctl(can_shm_id, IPC_RMID, NULL);
close(can_fd);
return NULL;
}
fchmod(can_lk, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if(new_shm) {
struct timeval tmv;
struct timezone tz;
gettimeofday(&tmv,&tz);
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
can_pid = 0;
can_open = -1;
rx_buff_pntr = 0;
for(i=0; i<CAN_RX_SIZE; i++) {
rx_buff[i].id = 0;
rx_buff[i].timestamp = tmv;
}
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
}
signal(SIGHUP, can_exit);
signal(SIGINT, can_exit);
signal(SIGQUIT,can_exit);
signal(SIGTERM,can_exit);
return(can_ctrl_addr);
}
/* CAN "Rx to buff" process */
void *start_can_io() {
set_server_mode(1);
init_can_io();
if(can_io_ok()) {
can_prtime(stderr);
fprintf(stderr,"CAN I/O process(%d) already running!\n",can_pid);
sleep(1);
can_prtime(stderr);
fprintf(stderr,"New CAN I/O process(%d) exiting...!\n",getpid());
exit(0);
}
if( can_fd < 0 ) {
can_prtime(stderr);
fprintf(stderr,"Error opening CAN device %s\n", can_dev);
shmctl(can_shm_id, IPC_RMID, NULL);
exit(0);
}
can_pid = getpid();
can_open = can_fd;
signal(SIGHUP, can_abort);
signal(SIGINT, can_abort);
signal(SIGQUIT,can_abort);
signal(SIGFPE, can_abort);
signal(SIGPIPE,can_abort);
signal(SIGSEGV,can_abort);
signal(SIGALRM, SIG_IGN);
signal(SIGTERM,can_abort);
if(shmctl(can_shm_id, SHM_LOCK, NULL) < 0)
perror("CAN I/O: can't prevents swapping of Rx-buffer area");
while(1) {
int n;
canmsg_t rx;
if(!can_io_shm_ok()) {can_delay(0.3); continue;}
n = can_wait(can_fd, 0.3);
if(n < 0) sleep(1);
if(n <= 0) continue;
// do {
static struct timeval tm = {0,0};
n = read(can_fd, &rx, sizeof(canmsg_t));
if(n < 0)
perror("CAN Rx error");
else if(n > 0) {
/* work around the timestamp bug in old driver version */
while((double)rx.timestamp.tv_sec+(double)rx.timestamp.tv_usec/1e6 < (double)tm.tv_sec+(double)tm.tv_usec/1e6) {
rx.timestamp.tv_usec += 10000;
if(rx.timestamp.tv_usec > 1000000) {
rx.timestamp.tv_sec++;
rx.timestamp.tv_usec -= 1000000;
}
}
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
rx_buff[rx_buff_pntr] = rx;
rx_buff_pntr = (rx_buff_pntr + 1) % CAN_RX_SIZE;
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
//fprintf(stderr,"%d read(id=%02x,len=%d)\n",rx_buff_pntr,rx.id,rx.length);fflush(stderr);
/*fprintf(stderr,"reading CAN: 1 frame\n");*/
} else {
fprintf(stderr,"reading CAN: nothing\n");fflush(stderr);
}
// } while(n>0);
}
}
/* put CAN-frame to recv-buffer */
void can_put_buff_frame(double rtime, int id, int length, unsigned char data[]) {
int i;
canmsg_t rx;
int sec = (int)rtime;
if(!server_mode) return;
if(length<0) length=0;
if(length>8) length=8;
rx.id=id;
rx.length=length;
for(i=0; i<length; i++) rx.data[i]=data[i];
rx.timestamp.tv_sec = sec;
rx.timestamp.tv_usec = (int)((rtime-sec)*1000000.);
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
rx_buff[rx_buff_pntr] = rx;
rx_buff_pntr = (rx_buff_pntr + 1) % CAN_RX_SIZE;
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
}
/* ÷ÓÅ ÎÏÒÍÁÌØÎÏ Ó SHM-ÂÕÆÅÒÏÍ CAN-I/O ÐÒÏÃÅÓÓÁ */
int can_io_shm_ok() {
return(can_pid>0 && can_open>0);
}
/* ÷ÓÅ ÎÏÒÍÁÌØÎÏ Ó CAN-I/O ÐÒÏÃÅÓÓÏÍ */
/* (ÎÏ ÎÁÄÏ ÂÙÔØ ÓÕÐÅÒ-ÀÚÅÒÏÍ!) */
int can_io_ok() {
return(can_io_shm_ok() && (my_uid!=0||kill(can_pid, 0)==0));
}
/* ÷ÏÚÍÏÖÎÁ ÒÁÂÏÔÁ c CAN ÄÌÑ ËÌÉÅÎÔÁ */
int can_ok() {
return(can_io_shm_ok());
}
/* wait for CAN-frame */
int can_wait(int fd, double tout)
{
int nfd,width;
struct timeval tv;
fd_set readfds;
if(fd==0 && tout>=0.01) {
double dt = can_dsleep(tout);
if(dt>0.) can_dsleep(dt);
return(0);
}
if(fd<0) fd=can_fd;
if(fd>0) {
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
width = fd+1;
} else
width = 0;
tv.tv_sec = (int)tout;
tv.tv_usec = (int)((tout - tv.tv_sec)*1000000.+0.9);
slipping:
if(fd>0 && can_fd>0)
nfd = select(width, &readfds, (fd_set *)NULL, (fd_set *)NULL, &tv);
else
nfd = select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, &tv);
if(nfd < 0) {
if(errno == EINTR)
goto slipping;
perror("Error in can_wait(){ select() }");
return(-1);
} else if(nfd == 0) /* timeout! */
return(0);
if(fd>0 && FD_ISSET(fd, &readfds)) /* Rx frame! */
return(1);
return(0);
}
/* cleanup recv-buffer in client process */
void can_clean_recv(int *pbuf, double *rtime) {
struct timeval tmv;
struct timezone tz;
gettimeofday(&tmv,&tz);
*pbuf = rx_buff_pntr;
*rtime = tmv.tv_sec + (double)tmv.tv_usec/1000000.;
}
/* find next rx-frame in recv-buffer for client process */
int can_recv_frame(int *pbuf, double *rtime,
int *id, int *length, unsigned char data[]) {
return(can_get_buff_frame(pbuf, rtime, id, length, data));
}
int can_get_buff_frame(int *pbuf, double *rtime,
int *id, int *length, unsigned char data[]) {
while(*pbuf != rx_buff_pntr) {
canmsg_t *rx = &rx_buff[*pbuf];
struct timeval *tv = &rx->timestamp;
double t_rx;
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
t_rx = tv->tv_sec + (double)tv->tv_usec/1000000.;
if(t_rx+1. >= *rtime) {
int i;
*id = rx->id;
*length = rx->length;
for(i = 0; i < *length; i++)
data[i] = rx->data[i];
*rtime = t_rx;
*pbuf = (*pbuf + 1) % CAN_RX_SIZE;
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
return(1);
}
*pbuf = (*pbuf + 1) % CAN_RX_SIZE;
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
}
return(0);
}
/* send tx-frame from client process */
/* to CAN-driver or to output queue */
int can_send_frame(int id, int length, unsigned char data[]) {
int i, ret=1;
if(can_fd<0 && canout.id<0)
return(0);
if(length>8) length=8;
if(length<0) length=0;
if(can_fd>=0) {
canmsg_t tx;
tx.id=id;
tx.cob=0;
tx.flags=0;
tx.length=length;
for(i=0;i<length;i++) tx.data[i]=data[i];
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
ret = write(can_fd, &tx, sizeof(canmsg_t));
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
if(server_mode)
/* copy tx CAN-frame back to recv-buffer */
can_put_buff_frame(can_dtime(), id, length, data);
} else if(canout.id>=0) {
struct my_msgbuf mbuf;
mbuf.src_pid = getpid();
mbuf.src_ip = 0;
mbuf.acckey = canout.acckey;
mbuf.mtype = id;
for(i=0;i<length;i++) mbuf.mtext[i]=data[i];
msgsnd( canout.id, (struct msgbuf *)&mbuf, length+12, IPC_NOWAIT);
}
return(ret);
}
static void can_abort(int sig) {
char ss[10];
struct shmid_ds buf;
if(sig) signal(sig,SIG_IGN);
if(!server_mode) can_exit(sig);
switch (sig) {
case 0 : strcpy(ss," "); break;
case SIGHUP : strcpy(ss,"SIGHUP -"); break;
case SIGINT : strcpy(ss,"SIGINT -"); break;
case SIGQUIT: strcpy(ss,"SIGQUIT -"); break;
case SIGFPE : strcpy(ss,"SIGFPE -"); break;
case SIGPIPE: strcpy(ss,"SIGPIPE -"); break;
case SIGSEGV: strcpy(ss,"SIGSEGV -"); break;
case SIGTERM: strcpy(ss,"SIGTERM -"); break;
default: sprintf(ss,"SIG_%d -",sig); break;
}
switch (sig) {
default:
case SIGHUP :
case SIGINT :
can_prtime(stderr);
fprintf(stderr,"CAN I/O: %s Ignore .....\n",ss);
fflush(stderr);
signal(sig, can_abort);
return;
case SIGPIPE:
case SIGQUIT:
case SIGFPE :
case SIGSEGV:
case SIGTERM:
signal(SIGALRM, can_abort);
alarm(2);
can_prtime(stderr);
fprintf(stderr,"CAN I/O: %s process should stop after 2sec delay...\n",ss);
fflush(stderr);
close(can_fd);
can_fd = can_open = -1;
return;
case SIGALRM:
can_prtime(stderr);
fprintf(stderr,"CAN I/O: process stop!\n");
fflush(stderr);
close(can_lk);
can_lk = -1;
can_pid = 0;
shmdt(can_shm_addr);
shmctl(can_shm_id, IPC_STAT, &buf);
if(buf.shm_nattch==0) {
shmctl(can_shm_id, SHM_UNLOCK, NULL);
shmctl(can_shm_id, IPC_RMID, NULL);
}
exit(sig);
}
}
void can_exit(int sig) {
char ss[12];
struct shmid_ds buf;
if(sig) signal(sig,SIG_IGN);
if(server_mode) can_abort(sig);
switch (sig) {
case 0 : strcpy(ss,"Exiting - "); break;
case SIGHUP : strcpy(ss,"SIGHUP -"); break;
case SIGINT : strcpy(ss,"SIGINT -"); break;
case SIGQUIT: strcpy(ss,"SIGQUIT -"); break;
case SIGFPE : strcpy(ss,"SIGFPE -"); break;
case SIGPIPE: strcpy(ss,"SIGPIPE -"); break;
case SIGSEGV: strcpy(ss,"SIGSEGV -"); break;
case SIGTERM: strcpy(ss,"SIGTERM -"); break;
default: sprintf(ss,"SIG_%d -",sig); break;
}
switch (sig) {
default:
case SIGHUP :
can_prtime(stderr);
fprintf(stderr,"%s Ignore .....\n",ss);
fflush(stderr);
signal(sig, can_exit);
return;
case 0:
case SIGINT :
case SIGPIPE:
case SIGQUIT:
case SIGFPE :
case SIGSEGV:
case SIGTERM:
if(can_fd>=0) close(can_fd);
can_prtime(stderr);
fprintf(stderr,"%s process stop!\n",ss);
fflush(stderr);
close(can_lk);
shmdt(can_shm_addr);
shmctl(can_shm_id, IPC_STAT, &buf);
if(buf.shm_nattch==0)
shmctl(can_shm_id, IPC_RMID, NULL);
exit(sig);
}
}
char *time2asc(double t)
{
static char stmp[10][20];
static int itmp=0;
char *lin = stmp[itmp];
int h, min;
double sec;
h = (int)(t/3600.);
min = (int)((t - (double)h*3600.)/60.);
sec = t - (double)h*3600. - (double)min*60.;
h %= 24;
sprintf(lin, "%02d:%02d:%09.6f", h,min,sec);
itmp = (itmp+1)%10;
return lin;
}
double can_dsleep(double dt) {
struct timespec ts,tsr;
ts.tv_sec = (time_t)dt;
ts.tv_nsec = (long)((dt-ts.tv_sec)*1e9);
nanosleep(&ts,&tsr);
return((double)ts.tv_sec + (double)ts.tv_nsec/1e9);
}
double can_dtime() {
struct timeval ct;
struct timezone tz;
gettimeofday(&ct, &tz);
return ((double)ct.tv_sec + (double)ct.tv_usec/1e6);
}
char *can_atime() {return(time2asc(can_dtime()));}
void can_prtime(FILE *fd) {
static double otime=0.0;
double ntime=can_dtime();
time_t itime = (int)ntime;
if(otime==0.0) tzset();
ntime -= (double)timezone;
if((((int)ntime)%(24*3600) < ((int)otime)%(24*3600)) || otime==0.0)
fprintf(fd,"========================\n%s",ctime(&itime));
fprintf(fd,"%s ",time2asc(ntime));
otime=ntime;
}

29
Socket_CAN/can_io.h Normal file
View File

@ -0,0 +1,29 @@
#define CAN_CTLR_SIZE 300 /* size of client process shared area */
#define CAN_RX_SIZE 1000 /* max. # frames in Rx-buffer */
int can_wait(int fd, double tout);
#define can_delay(Tout) can_wait(0, Tout)
void set_server_mode(int mode);
int can_server();
int can_card();
int can_gate();
double can_gate_time_offset();
unsigned long get_acckey();
void *init_can_io();
void *start_can_io();
void can_put_buff_frame(double rtime, int id, int length, unsigned char data[]);
int can_io_ok();
int can_io_shm_ok();
int can_ok();
void can_clean_recv(int *pbuf, double *rtime);
int can_get_buff_frame(int *pbuf, double *rtime,
int *id, int *length, unsigned char data[]);
int can_recv_frame(int *pbuf, double *rtime,
int *id, int *length, unsigned char data[]);
int can_send_frame(int id, int length, unsigned char data[]);
void can_exit(int sig);
char *time2asc(double t);
double can_dsleep(double dt);
double can_dtime();
char *can_atime();
void can_prtime(FILE *fd);

39
Socket_CAN/canbus.h Normal file
View File

@ -0,0 +1,39 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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
#ifndef CANBUS_H__
#define CANBUS_H__
#include <stdint.h>
// CAN packet MAGICK
#define CANMAGICK (0xdeadbeef)
// BTA SEW mask
#define ID_MASK (0xD80)
// BTA SEW-dome frames
#define ID_DOME (0x580)
typedef struct{
uint32_t magick;
uint16_t ID;
uint8_t len;
uint8_t data[8];
} can_packet;
#endif // CANBUS_H__

76
Socket_CAN/cansock.c Normal file
View File

@ -0,0 +1,76 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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/>.
*/
int readCAN(int sock, struct can_frame *frame){
fd_set fds;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 1000; // wait not more than 1millisecond
FD_ZERO(&fds);
FD_SET(sock, &fds);
do{
int rc = select(sock+1, &fds, NULL, NULL, &timeout);
if(rc < 0){
if(errno != EINTR){
WARN("select()");
return 0;
}
continue;
}
break;
}while(1);
if(FD_ISSET(sock, &fds)){
return read(sock, frame, sizeof(struct can_frame));
}
return 0;
}
int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if(sock < 0) ERR("Can't create socket");
struct sockaddr_can addr;
struct ifreq ifr;
strcpy(ifr.ifr_name, "can0");
if(ioctl(sock, SIOCGIFINDEX, &ifr) < 0) ERR("ioctl()");
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) ERR("bind()");
int nbytes = 0;
struct can_frame frame = {0};
double dt = dtime();
do{
nbytes = readCAN(sock, &frame);
if(nbytes < 0) ERR("can raw socket read");
}while(nbytes == 0 && dtime() - dt < 10.);
printf("got frame len=%d id=%d", frame.can_dlc, frame.can_id);
if(frame.can_dlc) printf(", data: ");
for(int i = 0; i < frame.can_dlc; ++i)
printf("0x%02x ", frame.data[i]);
printf("\n");
frame.can_dlc = 6;
frame.can_id = 0xaa;
const uint8_t d[] = {1, 2, 3, 4, 5, 6, 0, 0};
memcpy(&frame.data, d, 8);
int n = write(sock, &frame, sizeof(struct can_frame));
if(sizeof(struct can_frame) != n){
printf("n=%d\n", n);
WARN("write()");
}
close(sock);

28
Socket_CAN/cert.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCocrggX/uMcOfG
bVqeVZpqzX/RPl0YZf9R5ym0YhCeZPj4Ribl59vLsjFShR0PtwV3pCffZUazQJHs
yA/UxrIG3JN9sk5B3+SdBUdmFDXcjI/vTYAFjqa6cqQ7GqQENNSKnvIW2PWe+TS8
KepPQKEqdXLb2k7MTTLx7sFeS3RyNHnQ9V1Ef7ZTjmYGrGe6QQA1irf3QgZ8c47Q
oIUQBEIj1JFyXhChU15BRwVEcAGDrcp8QCYjSba8ac+eBDmn/c8Ojrw3MIsgB38q
I18xXIfuf4jSN+hZjMktFr/IZwePOUFqQjSNkMlUsZvUdkFWfEoEamP3b5Pft1IC
okeg4+rDAgMBAAECggEAYHRoOKmdadrQ7Q97H0UXSeboNNHIDwuAus5qVA7/QyPA
aLIK3gT5F9euZHkynegIKm0GI+ZjKItlw7b/UbCBCmrNzeEG7LOevcbjDnGuMd8P
IFE5aHU5xXOV1P45QutZdRL0vt53LxO1/bTmpYD1iFF+dqO4EUZ3UI+NjEgaorW5
x9kZwV6PE4Cnfcx8kdDNvBrQDs/AXJz3zNNsIU7cEFAVLvtkDnw5+aa0O52lLa+D
2vtpctxCyPrKtV5Z7Cgd+KkvXKDYbSgrld/Pb6Bhwj+cD39s5Jd/JRBnhpprHq5Y
X8nCs0rZP901rTbGPRcOAu1qUrgdtEyFyiAMnCBGWQKBgQDWQGD87w2E7cbNxZAp
nYBc4LizChtvFRu5hDmbblhvRt93XWh4H5wrJuKbFBM/O38KHy/9GCEVv8XwCkgd
DKhNlAPwMkszxYkVw4cmc3FwKEQhfX4KpzwfrWFbPFTGlCtt3EAP5g6wvXYJeWZZ
T+KXCOzWG5b+v38LLZzxctPp7QKBgQDJRYHKK4txrbXIfqSILk1k9C3+8H6lPFfk
S0gemPP7TuE2jHH/OsK36qtgH+i6pWrqZVKL9QmpFugiSvI/SP0DWk7t17iFnJ6s
bltn8gftASRnx8yFVZBYUO9TbmriS0ZRJG8Yz7f79jj2TGDunBVgeu560X6Syjyk
WpzEjEPRbwKBgFL2MNggNOrxK5cIVi9XFppgRgTF+COGV+r5IVlnXAUSu3s8BzTk
gJNRBlQobN+CSUoBE5L8YetLC/lL8eqVuSH5G6FJyEbuyYtM4CtqblWQsfkJ3+F+
KlDV0SoD7YvLWhm7PG8rlSqo4mj+wjv5K/Nx+Etb+ZcBTc9lRS1VWmttAoGBAMSC
GnA7B5Bb26n/C8DyBBpW1TmdsOi++8knPyiwiTWKFBTgFsTsqARNGDlnrh/dNX/Y
oTmIaoAun0IsDkx/hJfrajiJb9zzx2/u50ubYOWjQdoZlrNvkNjJXIEGw1Bh0iuS
+O0ukSFtirveYp1UwwJJw+Eh/QRwc7i7x2eW0vf9AoGAVPIZuGkwafu4yfsKBKqx
Qvmgm7+iCywBz6jj70Zk1uS2+l/Kvc1sFD3vF9QS6LzxWgotMqqTR+JTLqMhw/SW
nTSoickIlkchWWnOF0tg1Gx9qHj9iTXufSwAqtYa18Srf/lQKB/HM24BSugGAxK/
W+UrzBJIZnVyR/HkDSk5VWY=
-----END PRIVATE KEY-----

21
Socket_CAN/cert.pem Normal file
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgIUBo309dQtnbIqVJIYQ0929yXmRB0wDQYJKoZIhvcNAQEL
BQAwPzELMAkGA1UEBhMCUlUxDTALBgNVBAgMBEtDaFIxDzANBgNVBAcMBkJ1a292
bzEQMA4GA1UECgwHU0FPIFJBUzAeFw0yMTEyMTYwODA3NDNaFw00OTA4MTEwODA3
NDNaMD8xCzAJBgNVBAYTAlJVMQ0wCwYDVQQIDARLQ2hSMQ8wDQYDVQQHDAZCdWtv
dm8xEDAOBgNVBAoMB1NBTyBSQVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCocrggX/uMcOfGbVqeVZpqzX/RPl0YZf9R5ym0YhCeZPj4Ribl59vLsjFS
hR0PtwV3pCffZUazQJHsyA/UxrIG3JN9sk5B3+SdBUdmFDXcjI/vTYAFjqa6cqQ7
GqQENNSKnvIW2PWe+TS8KepPQKEqdXLb2k7MTTLx7sFeS3RyNHnQ9V1Ef7ZTjmYG
rGe6QQA1irf3QgZ8c47QoIUQBEIj1JFyXhChU15BRwVEcAGDrcp8QCYjSba8ac+e
BDmn/c8Ojrw3MIsgB38qI18xXIfuf4jSN+hZjMktFr/IZwePOUFqQjSNkMlUsZvU
dkFWfEoEamP3b5Pft1ICokeg4+rDAgMBAAGjUzBRMB0GA1UdDgQWBBRqwlIH5zOT
171ywLEMmP1WfJ3ZdjAfBgNVHSMEGDAWgBRqwlIH5zOT171ywLEMmP1WfJ3ZdjAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAwgUv4mlGxwqmyQsTg
LHVppuLl4TA1EMrUuvJfSBClfStnWiCDJmNMn7OMAuNVmpLZHz2gPbAz5fHRnm0N
Ey3nQNS0X18/jq4NCjaONoSjcRxHDbw0V0E2Kap+t9YZbzw83FWKcJM3WKMAL51p
xiUWQ9bP5E7CFY+j+kcty3HfSgoGcEqanTK94QQqlKxczI1kKkR/Bdow3jTk6PG7
fMPU6c8FL3NkRacKGoc5VNq8A7HI5Hll4gu1fYxXVYnkKc45NrLsipdhInFNJOmx
O16CWm+S/YEJFVio6Eke9FhLehaHagY0oMhadaV4sxtTDXGosfoI/XorGsYE79sU
uqgK
-----END CERTIFICATE-----

43
Socket_CAN/client.c Normal file
View File

@ -0,0 +1,43 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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 <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <net/if.h>
#include <usefull_macros.h>
#include "cmdlnopts.h"
#include "daemon.h"
#include "sslsock.h"
int main(int argc, char **argv){
char *self = argv[0];
initial_setup();
parse_args(argc, argv);
// check args
if(!G.serverhost) ERR("Point server IP address");
#ifdef EBUG
printf("Server: %s\n", G.serverhost);
#endif
return start_daemon(self);
}

24
Socket_CAN/client.h Normal file
View File

@ -0,0 +1,24 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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
#ifndef CLIENT_H__
#define CLIENT_H__
#include "canbus.h"
#endif // CLIENT_H__

89
Socket_CAN/cmdlnopts.c Normal file
View File

@ -0,0 +1,89 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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 <stdio.h>
#include <string.h>
#include <strings.h>
#include <usefull_macros.h>
#include "cmdlnopts.h"
/*
* here are global parameters initialisation
*/
static int help;
glob_pars G;
// DEFAULTS
// default global parameters
static glob_pars const Gdefault = {
.pidfile = DEFAULT_PIDFILE,
.port = DEFAULT_PORT,
.cert = "cert.pem",
.key = "cert.key",
};
/*
* 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: cert.pem)")},
{"key", NEED_ARG, NULL, 'k', arg_string, APTR(&G.key), _("path to SSL key (default: cert.key)")},
{"ca", NEED_ARG, NULL, 'a', arg_string, APTR(&G.ca), _("path to SSL ca (default: ca.pem)")},
{"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)")},
#ifdef SERVER
#endif
#ifdef CLIENT
{"server", NEED_ARG, NULL, 'i', arg_string, APTR(&G.serverhost), _("server IP address")},
#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;
void *ptr;
ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
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]);
}
}

54
Socket_CAN/cmdlnopts.h Normal file
View File

@ -0,0 +1,54 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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
#ifndef CMDLNOPTS_H__
#define CMDLNOPTS_H__
// default PID filename:
#ifndef DEFAULT_PIDFILE
#define DEFAULT_PIDFILE "/tmp/soccan.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 *ca; // ca
char *port; // port number
int verbose; // logfile verbose level
#ifdef SERVER
#endif
#ifdef CLIENT
char *serverhost; // server IP address
int speed; // connection speed
#endif
} glob_pars;
extern glob_pars G;
void parse_args(int argc, char **argv);
#endif // CMDLNOPTS_H__

99
Socket_CAN/daemon.c Normal file
View File

@ -0,0 +1,99 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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 = 0;
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
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
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();
}

25
Socket_CAN/daemon.h Normal file
View File

@ -0,0 +1,25 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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
#ifndef DAEMON_H__
#define DAEMON_H__
int start_daemon(char *self);
#endif // DAEMON_H__

5
Socket_CAN/diff/clientscript Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
for ((x=0; x<10; ++x)); do
socat TCP-LISTEN:2000 PTY,link=/tmp/ttyX0,raw,crnl
done

6
Socket_CAN/diff/serverscript Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
#for ((x=0; x<10; ++x)); do
socat pty,link=/tmp/ttyX0,waitslave tcp:127.0.0.1:2002 &
ssh -L 2002:robotel1:2000 -N -f robotel1
#done

121
Socket_CAN/log.log Normal file
View File

@ -0,0 +1,121 @@
[ERR] 2021/12/20-09:51:56 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-09:51:57 SSL_accept()
[WARN] 2021/12/20-09:51:57 Child killed with sig=9
[ERR] 2021/12/20-09:54:18 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-09:54:19 SSL_accept()
[ERR] 2021/12/20-09:54:33 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-09:54:39 Child killed with sig=2
[ERR] 2021/12/20-09:55:27 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-09:55:30 Child killed with sig=2
[ERR] 2021/12/20-10:00:07 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-10:00:14 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-10:00:17 Child killed with sig=2
[WARN] 2021/12/20-11:30:15 Could not set the CA file location
[WARN] 2021/12/20-11:30:15 Child killed with sig=9
[ERR] 2021/12/20-11:31:48 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-11:31:48 SSL_accept()
[ERR] 2021/12/20-11:32:38 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-11:32:38 SSL_accept()
[WARN] 2021/12/20-11:33:54 Child killed with sig=2
[ERR] 2021/12/20-11:33:57 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-11:33:57 SSL_accept()
[WARN] 2021/12/20-11:37:21 Child killed with sig=2
[ERR] 2021/12/20-11:37:24 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-11:37:40 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-11:37:42 Child killed with sig=2
[ERR] 2021/12/20-11:38:03 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-11:38:03 SSL_accept()
[WARN] 2021/12/20-11:38:14 Child killed with sig=2
[ERR] 2021/12/20-14:44:30 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-14:44:30 SSL_accept()
[WARN] 2021/12/20-14:44:30 Child killed with sig=9
[ERR] 2021/12/20-14:44:38 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-14:44:38 SSL_accept()
[WARN] 2021/12/20-14:44:38 Child killed with sig=9
[ERR] 2021/12/20-14:44:53 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-14:45:16 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-14:46:13 Child killed with sig=2
[ERR] 2021/12/20-14:46:20 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-14:47:49 Child killed with sig=2
[ERR] 2021/12/20-14:47:52 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-14:50:22 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-14:50:24 Child killed with sig=2
[ERR] 2021/12/20-14:50:28 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-14:52:08 Child killed with sig=2
[ERR] 2021/12/20-14:52:28 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-14:53:09 Child killed with sig=2
[ERR] 2021/12/20-14:53:11 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-14:53:12 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/20-14:54:40 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-14:55:40 Child killed with sig=2
[ERR] 2021/12/20-14:55:42 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/20-14:56:01 Child killed with sig=2
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:21 SSL_accept()
[ERR] 2021/12/21-09:17:21 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-09:17:21 Child killed with sig=9
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:17:27 SSL_accept()
[WARN] 2021/12/21-09:17:27 Child killed with sig=9
[ERR] 2021/12/21-09:17:27 SSL_accept()
[ERR] 2021/12/21-09:17:27 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:32:32 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-09:34:25 Child killed with sig=2
[ERR] 2021/12/21-09:34:28 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-09:34:48 Child killed with sig=2
[ERR] 2021/12/21-09:36:00 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:36:00 SSL_accept()
[WARN] 2021/12/21-09:36:00 Child killed with sig=9
[ERR] 2021/12/21-09:44:24 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-09:55:33 Child killed with sig=2
[ERR] 2021/12/21-09:55:38 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:55:38 SSL_accept()
[WARN] 2021/12/21-09:55:38 Child killed with sig=9
[ERR] 2021/12/21-09:56:50 open_socket(): UNREACHABLE CODE REACHED!
[ERR] 2021/12/21-09:57:36 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-09:57:39 Child killed with sig=2
[ERR] 2021/12/21-09:58:13 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-09:58:36 Child killed with sig=2
[ERR] 2021/12/21-09:58:45 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-09:58:47 Child killed with sig=2
[ERR] 2021/12/21-10:03:33 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-10:03:40 Child killed with sig=2
[ERR] 2021/12/21-12:39:08 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-12:39:16 Child killed with sig=2
[ERR] 2021/12/21-12:40:52 open_socket(): UNREACHABLE CODE REACHED!
[WARN] 2021/12/21-12:41:02 Child killed with sig=2
[ERR] 2021/12/21-14:52:19 Can't init CAN bus, will try later
[WARN] 2021/12/21-14:52:27 Child killed with sig=2
[ERR] 2021/12/21-14:54:28 Can't init CAN bus, will try later
[WARN] 2021/12/21-14:54:50 Child killed with sig=2
[WARN] 2021/12/21-14:56:52 Child killed with sig=2
[WARN] 2021/12/21-14:57:08 Max amount of connections: disconnect fd=6
[WARN] 2021/12/21-14:57:12 Max amount of connections: disconnect fd=6
[WARN] 2021/12/21-14:57:20 Child killed with sig=2
[ERR] 2021/12/21-14:57:39 SSL error 5
[WARN] 2021/12/21-14:57:40 Child killed with sig=2
[ERR] 2021/12/21-16:27:13 SSL error 5
[WARN] 2021/12/21-16:27:16 Child killed with sig=2

74
Socket_CAN/server.c Normal file
View File

@ -0,0 +1,74 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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 "can_io.h"
#include "daemon.h"
#include "cmdlnopts.h"
#include "server.h"
#include "sslsock.h"
int can_inited = 0;
static int try2initBTAcan(){
static double t0 = 0., t1 = 0.;
can_inited = 0;
if(dtime() - t1 < 1.) return 0; // try not more then once per second
t1 = dtime();
if(!init_can_io() || !can_ok()){
if(dtime() - t0 > 30.){ // show errors not more than onse per 30s
LOGERR("Can't init CAN bus, will try later");
WARNX("Can't init CAN bus");
t0 = dtime();
}
}else{
can_inited = 1;
int rxpnt; double rxtime;
can_clean_recv(&rxpnt, &rxtime);
}
return can_inited;
}
// read next message from CAN
// @parameter pk - packet (allocated outside)
// @return pointer to pk or NULL if none/error
can_packet *readBTAcan(can_packet *pk){
if((!can_inited || !can_ok()) && !try2initBTAcan()) return NULL;
int rxpnt, idr, len;
double rxtime;
if(!can_recv_frame(&rxpnt, &rxtime, &idr, &len, pk->data)) return NULL;
if((idr & ID_MASK) == ID_DOME){
pk->ID = (uint16_t)idr;
pk->len = (uint8_t)len;
pk->magick = CANMAGICK;
return pk;
}
return NULL;
}
int main(int argc, char **argv){
char *self = argv[0];
initial_setup();
parse_args(argc, argv);
#ifdef EBUG
#endif
try2initBTAcan();
return start_daemon(self);
}

29
Socket_CAN/server.h Normal file
View File

@ -0,0 +1,29 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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
#ifndef SERVER_H__
#define SERVER_H__
#include "canbus.h"
can_packet *readBTAcan(can_packet *pk);
extern int can_inited;
#endif // SERVER_H__

BIN
Socket_CAN/soccanclient Executable file

Binary file not shown.

BIN
Socket_CAN/soccanserver Executable file

Binary file not shown.

322
Socket_CAN/sslsock.c Normal file
View File

@ -0,0 +1,322 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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 <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 <pthread.h>
#include <resolv.h>
#include <signal.h> // pthread_kill
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.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;
}
// return 0 if client disconnected
static int handle_connection(SSL *ssl){
char buf[1025];
int bytes = SSL_read(ssl, buf, sizeof(buf)-1);
int sd = SSL_get_fd(ssl);
if(bytes < 1){
int sslerr = SSL_get_error(ssl, bytes);
if(SSL_ERROR_WANT_READ == sslerr ||
SSL_ERROR_WANT_WRITE == sslerr) return 1; // empty call
LOGERR("SSL error %d", sslerr);
WARNX("SSL error %d", sslerr);
return 0;
}else{
buf[bytes] = '\0';
printf("Client %d msg: \"%s\"\n", sd, buf);
LOGDBG("fd=%d, message=%s", sd, buf);
snprintf(buf, 1024, "Hello, yout FD=%d", sd);
SSL_write(ssl, buf, strlen(buf));
}
return 1;
}
#endif
#ifdef CLIENT
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;
if(poll(&fds, 1, 1) < 0){ // wait no more than 1ms
LOGWARN("SSL_nbread(): poll() failed");
WARNX("poll()");
return 0;
}
if(fds.revents == POLLIN){
DBG("Got info in fd #%d", fd);
int bytes = SSL_read(ssl, buf, bufsz);
DBG("read %d bytes", bytes);
if(bytes == 0) return -1;
if(bytes < 0){
int sslerr = SSL_get_error(ssl, bytes);
if(SSL_ERROR_WANT_READ == sslerr ||
SSL_ERROR_WANT_WRITE == sslerr) return 0; // empty call
LOGERR("SSL error %d", sslerr);
WARNX("SSL error %d", sslerr);
return bytes;
}
return bytes;
}
return 0;
}
static int OpenConn(int port){
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);
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("connect()");
}
return sd;
}
static void clientproc(SSL_CTX *ctx, int fd){
FNAME();
SSL *ssl;
char buf[1024];
char acClientRequest[1024] = {0};
int bytes;
ssl = SSL_new(ctx);
SSL_set_fd(ssl, fd);
if(-1 == SSL_connect(ssl)){
LOGERR("SSL_connect()");
ERRX("SSL_connect() error");
}
int enable = 1;
if(ioctl(fd, FIONBIO, (void *)&enable) < 0){
LOGERR("Can't make socket nonblocking");
ERRX("ioctl()");
}
double t0 = dtime();
int N = 0;
while(1){
if(dtime() - t0 > 3.){
DBG("Sent test message");
sprintf(acClientRequest, "Test connection #%d", ++N);
SSL_write(ssl, acClientRequest, strlen(acClientRequest));
t0 = dtime();
}
bytes = SSL_nbread(ssl, buf, sizeof(buf));
if(bytes > 0){
if(bytes == sizeof(can_packet) && ((can_packet*)buf)->magick == CANMAGICK){ // can packet
can_packet *pk = (can_packet*)buf;
#ifdef EBUG
green("Got CAN message for ID 0x%X: ", pk->ID);
for(int i = 0; i < pk->len; ++i)
printf("0x%X ", pk->data[i]);
#endif
}else{ // text message
buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
}
}else if(bytes < 0){
LOGWARN("Server disconnected or other error");
ERRX("Disconnected");
}
}
SSL_free(ssl);
}
#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;
SSL_library_init();
SSL_CTX *ctx = InitCTX();
fd = OpenConn(atoi(G.port));
#ifdef SERVER
int enable = 1;
if(ioctl(fd, FIONBIO, (void *)&enable) < 0){
LOGERR("Can't make socket nonblocking");
ERRX("ioctl()");
}
int nfd = 1; // only one 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;
SSL *ssls[BACKLOG] = {0};
double t0 = dtime();
char buf[64]; int P = 0;
while(1){
// check CAN bus and send data to all connected
can_packet canpack;
if(readBTAcan(&canpack)){
DBG("GOT CAN packet");
for(int i = nfd-1; i > -1; --i)
SSL_write(ssls[i], &canpack, sizeof(canpack));
}
if(dtime() - t0 > 5. && nfd > 1){
DBG("send ping");
snprintf(buf, 63, "ping #%d", ++P);
for(int i = nfd-2; i > -1; --i)
SSL_write(ssls[i], buf, strlen(buf));
t0 = dtime();
}
poll(poll_set, nfd, 1); // max timeout - 1ms
// check main for accept()
if(poll_set[0].revents & POLLIN){
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int client = accept(fd, (struct sockaddr*)&addr, &len);
DBG("Connection: %s: %d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
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");
close(client);
}else{
SSL *ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
if(-1 == SSL_accept(ssl)){
LOGERR("SSL_accept()");
WARNX("SSL_accept()");
SSL_free(ssl);
}else{
ssls[nfd-1] = ssl;
memset(&poll_set[nfd], 0, sizeof(struct pollfd));
poll_set[nfd].fd = client;
poll_set[nfd].events = POLLIN;
++nfd;
}
}
}
// scan connections
for(int fdidx = 1; fdidx < nfd; ++fdidx){
if((poll_set[fdidx].revents & POLLIN) == 0) continue;
int fd = poll_set[fdidx].fd;
if(!handle_connection(ssls[fdidx-1])){ // socket closed
SSL_free(ssls[fdidx-1]);
DBG("Client fd=%d disconnected", fd);
LOGMSG("Client fd=%d disconnected", fd);
close(fd);
// move last FD to current position
poll_set[fdidx] = poll_set[nfd - 1];
ssls[fdidx - 1] = ssls[nfd - 2];
}
}
}
#else
clientproc(ctx, fd);
#endif
close(fd);
SSL_CTX_free(ctx);
return 0;
}

35
Socket_CAN/sslsock.h Normal file
View File

@ -0,0 +1,35 @@
/*
* This file is part of the SocketCAN project.
* Copyright 2021 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
#ifndef SSLSOCK_H__
#define SSLSOCK_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
int open_socket();
#endif // SSLSOCK_H__

45
serialsock/Makefile Normal file
View File

@ -0,0 +1,45 @@
# run `make DEF=...` to add extra defines
PROGRAM := serialsock
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
LDFLAGS += -lusefull_macros
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) $(LDFLAGS) $(OBJS) -o $(PROGRAM)
$(OBJDIR):
mkdir $(OBJDIR)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR)/%.o: %.c
@echo -e "\t\tCC $<"
$(CC) -MD -c $(LDFLAGS) $(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

1
serialsock/Readme Normal file
View File

@ -0,0 +1 @@
Socket server for my USB-CAN

View File

@ -0,0 +1 @@
-std=c17

View File

@ -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

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,5 @@
cmdlnopts.c
cmdlnopts.h
main.c
sersock.c
sersock.h

View File

@ -0,0 +1 @@
.

95
serialsock/cmdlnopts.c Normal file
View File

@ -0,0 +1,95 @@
/*
* This file is part of the canserver project.
* Copyright 2022 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 <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "cmdlnopts.h"
#include "usefull_macros.h"
// default PID filename:
#define DEFAULT_PIDFILE "/tmp/usbsock.pid"
#define DEFAULT_PORT "1234"
#define DEFAULT_SOCKPATH "\0canbus"
static int help;
static glob_pars G = {
.pidfile = DEFAULT_PIDFILE,
.speed = 9600,
.port = DEFAULT_PORT,
.path = DEFAULT_SOCKPATH,
.logfile = NULL // don't save logs
};
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")},
{"devpath", NEED_ARG, NULL, 'd', arg_string, APTR(&G.devpath), _("serial device path")},
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("serial device speed (default: 9600)")},
{"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")},
{"sockpath",NEED_ARG, NULL, 'f', arg_string, APTR(&G.path), _("socket path (start from \\0 for no files)")},
{"port", NEED_ARG, NULL, 'P', arg_string, APTR(&G.port), _("port to connect (default: " DEFAULT_PORT ")")},
{"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;
//printf("%s: ", __progname);
va_start(ar, fmt);
vprintf(fmt, ar);
va_end(ar);
printf("\n");
}

42
serialsock/cmdlnopts.h Normal file
View File

@ -0,0 +1,42 @@
/*
* This file is part of the canserver project.
* Copyright 2022 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
#ifndef CMDLNOPTS_H__
#define CMDLNOPTS_H__
/*
* here are some typedef's for global data
*/
typedef struct{
char *devpath; // path to serial device
char *pidfile; // name of PID file
char *logfile; // logging to this file
char *port; // network port
char *path; // path to socket file
int speed; // connection speed
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__

115
serialsock/main.c Normal file
View File

@ -0,0 +1,115 @@
/*
* This file is part of the canserver project.
* Copyright 2022 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 <arpa/inet.h>
#include <limits.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <usefull_macros.h>
#include "cmdlnopts.h"
#include "sersock.h"
static TTY_descr *dev = NULL;
static pid_t childpid = 0;
int server = 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(!server) restore_console();
else if(dev) close_tty(&dev);
if(sig){
DBG("Exit with signal %d", sig);
signal(sig, SIG_IGN);
LOGERR("Exit with signal %d", sig);
}else LOGERR("Exit");
if(GP->pidfile && server){
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) server = 0;
else if(!GP->devpath){
LOGERR("You should point serial device path");
ERRX("You should point serial device path");
}
int port = atoi(GP->port);
if(port < 1024 || port > 65535){
LOGERR("Wrong port value: %d", port);
WARNX("Wrong port value: %d", port);
return 1;
}
if(server) 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(server){
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
return start_socket(server, GP->path, &dev);
}

386
serialsock/sersock.c Normal file
View File

@ -0,0 +1,386 @@
/*
* This file is part of the canserver project.
* Copyright 2022 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 <arpa/inet.h>
#include <limits.h>
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h> // unix socket
#include <usefull_macros.h>
#include "cmdlnopts.h"
#include "sersock.h"
/**
* wait for answer from socket
* @param sock - socket fd
* @return 0 in case of error or timeout, 1 in case of socket ready
*/
static int waittoread(int sock){
fd_set fds;
struct timeval timeout;
int rc;
timeout.tv_sec = 0;
timeout.tv_usec = 100;
FD_ZERO(&fds);
FD_SET(sock, &fds);
do{
rc = select(sock+1, &fds, NULL, NULL, &timeout);
if(rc < 0){
if(errno != EINTR){
WARN("select()");
return 0;
}
continue;
}
break;
}while(1);
if(FD_ISSET(sock, &fds)){
//DBG("FD_ISSET");
return 1;
}
return 0;
}
// work with single client, return FALSE if disconnected
static int handle_socket(int sock, TTY_descr *d){
char buff[BUFLEN];
ssize_t rd = read(sock, buff, BUFLEN-1);;
DBG("Got %zd bytes", rd);
if(rd <= 0){ // error or disconnect
DBG("Nothing to read from fd %d (ret: %zd)", sock, rd);
return FALSE;
}
// add trailing zero to be on the safe side
buff[rd] = 0;
DBG("GOT: %s", buff);
ssize_t blen = strlen(buff);
if(blen != write(d->comfd, buff, blen)){
LOGWARN("write()");
WARN("write()");
}
if(buff[blen-1] == '\n') buff[blen-1] = 0;
LOGMSG("CLIENT_%d: %s", sock, buff);
return TRUE;
}
/**
* check data from fd
* @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;
}
/**
* @brief getserdata - read data (ending by '\n') from serial device
* @param D (i) - device
* @param len (o) - amount of butes read (-1 if disconnected)
* @return pointer to data buffer or NULL if none
*/
static char *getserdata(TTY_descr *D, int *len){
static char serbuf[BUFLEN], *ptr = serbuf;
static size_t blen = BUFLEN - 1;
if(!D || D->comfd < 0) return NULL;
char *nl = NULL;
do{
int s = canberead(D->comfd);
if(s == 0) break;
if(s < 0){ // interrupted?
if(len) *len = 0;
return NULL;
}
ssize_t l = read(D->comfd, ptr, blen);
if(l < 1){ // disconnected
DBG("device disconnected");
if(len) *len = -1;
return NULL;
}
ptr[l] = 0;
DBG("GOT %zd bytes: '%s'", l, ptr);
nl = strchr(ptr, '\n');
ptr += l;
blen -= l;
if(nl){
DBG("Got newline");
break;
}
}while(blen);
// recalculate newline from the beginning (what if old data stays there?)
nl = strchr(serbuf, '\n');
if(blen && !nl){
if(len) *len = 0;
return NULL;
}
// in case of overflow send buffer without trailing '\n'
int L;
if(nl) L = nl - serbuf + 1; // get line to '\n'
else L = strlen(serbuf); // get all buffer
if(len) *len = L;
memcpy(D->buf, serbuf, L); // copy all + trailing zero
D->buflen = L;
D->buf[L] = 0;
DBG("Put to buf %d bytes: '%s'", L, D->buf);
if(nl){
L = ptr - nl - 1; // symbols after newline
if(L > 0){ // there's some data after '\n' -> put it into the beginning
memmove(serbuf, nl+1, L);
blen = BUFLEN - 1 - L;
ptr = serbuf + L;
*ptr = 0;
DBG("now serbuf is '%s'", serbuf);
}else{
blen = BUFLEN - 1;
ptr = serbuf;
*ptr = 0;
}
}else{
blen = BUFLEN - 1;
ptr = serbuf;
*ptr = 0;
}
return D->buf;
}
static void server_(int sock, TTY_descr *d){
if(listen(sock, MAXCLIENTS) == -1){
WARN("listen");
LOGWARN("listen");
return;
}
int enable = 1;
if(ioctl(sock, FIONBIO, (void *)&enable) < 0){ // make socket nonblocking
LOGERR("Can't make socket nonblocking");
ERRX("ioctl()");
}
int nfd = 1; // only one socket @start
struct pollfd poll_set[MAXCLIENTS+1];
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("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;
}
}
int l = -1;
char *serdata = getserdata(d, &l);
if(l < 0){
LOGERR("Serial device disconnected");
ERRX("Serial device disconnected");
}
if(serdata){
for(int i = 1; i < nfd; ++i)
if(l != write(poll_set[i].fd, serdata, l)){
LOGWARN("write()");
WARN("write()");
}
if(serdata[l-1] == '\n') serdata[l-1] = 0;
LOGMSG("SERIAL: %s", serdata);
}
// scan connections
for(int fdidx = 1; fdidx < nfd; ++fdidx){
if((poll_set[fdidx].revents & POLLIN) == 0) continue;
int fd = poll_set[fdidx].fd;
if(!handle_socket(fd, d)){ // socket closed
DBG("Client fd=%d disconnected", fd);
LOGMSG("Client fd=%d disconnected", fd);
close(fd);
// move last FD to current position
poll_set[fdidx] = poll_set[nfd - 1];
--nfd;
}
}
}
}
// read console char - for client
static int rc(){
int rb;
struct timeval tv;
int retval;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
tv.tv_sec = 0; tv.tv_usec = 100;
retval = select(1, &rfds, NULL, NULL, &tv);
if(!retval) rb = 0;
else {
if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar();
else rb = 0;
}
return rb;
}
/**
* @brief mygetline - silently and non-blocking getline
* @return zero-terminated string with '\n' at end (or without in case of overflow)
*/
static char *mygetline(){
static char buf[BUFLEN+1];
static int i = 0;
while(i < BUFLEN){
char rd = rc();
if(!rd) return NULL;
if(rd == 0x7f && i){ // backspace
buf[--i] = 0;
printf("\b \b");
}else{
buf[i++] = rd;
printf("%c", rd);
}
fflush(stdout);
if(rd == '\n') break;
}
buf[i] = 0;
i = 0;
return buf;
}
static void client_(int sock){
setup_con(); // convert console mode into non-canon
int Bufsiz = BUFLEN;
char *recvBuff = MALLOC(char, Bufsiz);
while(1){
char *msg = mygetline();
if(msg){
ssize_t L = strlen(msg);
if(send(sock, msg, L, 0) != L){
WARN("send");
WARN("send");
}
if(msg[L-1] == '\n') msg[L-1] = 0;
LOGMSG("TERMINAL: %s", msg);
}
if(!waittoread(sock)) continue;
int n = read(sock, recvBuff, Bufsiz-1);
if(n == 0){
WARNX("Server disconnected");
signals(0);
}
recvBuff[n] = 0;
printf("%s", recvBuff);
if(recvBuff[n-1] == '\n') recvBuff[n-1] = 0;
LOGMSG("SERIAL: %s", recvBuff);
}
}
/**
* @brief openserialdev - open connection to serial device
* @param path (i) - path to device
* @param speed - connection speed
* @return pointer to device structure if all OK
*/
static TTY_descr *openserialdev(char *path, int speed){
TTY_descr *d = new_tty(path, speed, BUFLEN);
DBG("Device created");
if(!d || !(tty_open(d, 1))){
WARN("Can't open device %s", path);
LOGWARN("Can't open device %s", path);
return NULL;
}
DBG("device opened");
return d;
}
int start_socket(int server, char *path, TTY_descr **dev){
if(server){
if(!dev) return 1;
if(!(*dev = openserialdev(GP->devpath, GP->speed))){
LOGERR("Can't open serial device %s", GP->devpath);
ERR("Can't open serial device %s", GP->devpath);
}
}
int sock = -1;
int reuseaddr = 1;
struct sockaddr_un saddr = {0};
saddr.sun_family = AF_UNIX;
strncpy(saddr.sun_path, path, 106); // if sun_path[0] == 0 we don't create a file
if((sock = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0){ // or SOCK_STREAM?
LOGERR("socket()");
ERR("socket()");
}
if(server){
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
LOGWARN("setsockopt");
WARN("setsockopt");
}
if(bind(sock, &saddr, sizeof(saddr)) == -1){
close(sock);
LOGERR("bind");
ERR("bind");
}
}else{
if(connect(sock, &saddr, sizeof(saddr)) == -1){
LOGERR("connect()");
ERR("connect()");
}
}
if(server) server_(sock, *dev);
else client_(sock);
close(sock);
signals(0);
return 0;
}

31
serialsock/sersock.h Normal file
View File

@ -0,0 +1,31 @@
/*
* This file is part of the canserver project.
* Copyright 2022 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
#ifndef SERSOCK_H__
#define SERSOCK_H__
#define BUFLEN (1024)
// Max amount of connections
#define MAXCLIENTS (30)
#include <usefull_macros.h>
int start_socket(int server, char *path, TTY_descr **dev);
#endif // SERSOCK_H__