Skip to content

Commit

Permalink
Improve signal and thread handling (#36)
Browse files Browse the repository at this point in the history
Improve signal and thread handling

Signed-off-by: Joakim Roubert <joakimr@axis.com>
  • Loading branch information
joakimr-axis authored Sep 5, 2023
1 parent 72c395c commit ef335dc
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 30 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [2022] [Axis Communications AB]
Copyright [2023] [Axis Communications AB]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*Copyright (C) 2022, Axis Communications AB, Lund, Sweden. All Rights Reserved.*
*Copyright (C) 2023, Axis Communications AB, Lund, Sweden. All Rights Reserved.*

# Modbus ACAP

Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"vendor": "Axis Communications AB",
"embeddedSdkVersion": "2.0",
"runMode": "respawn",
"version": "1.1.0"
"version": "1.1.1"
},
"configuration": {
"paramConfig": [
Expand Down
2 changes: 1 addition & 1 deletion modbus_client.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2023, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion modbus_client.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2023, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
26 changes: 21 additions & 5 deletions modbus_server.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2023, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,7 @@

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <modbus.h>
#include <pthread.h>
#include <unistd.h>
Expand All @@ -26,13 +27,14 @@
static gboolean run_server = FALSE;
static pthread_t modbus_server_thread_id = -1;
static guint32 modbus_port = 0;
static modbus_t *srv_ctx = NULL;

static void *run_modbus_server(void *run)
{
assert(NULL != run);
modbus_t *srv_ctx;
modbus_mapping_t *mb_mapping = NULL;
int s;
int flags;

assert(1024 <= modbus_port && 65535 >= modbus_port);
LOG_I("Trying to create Modbus TCP context for all IP addresss and port %u ...", modbus_port);
Expand All @@ -51,13 +53,26 @@ static void *run_modbus_server(void *run)
goto server_exit;
}

LOG_I("Accept Modbus TCP connection ...");
if (-1 == modbus_tcp_accept(srv_ctx, &s))
flags = fcntl(s, F_GETFL, 0);
if (-1 == fcntl(s, F_SETFL, flags | O_NONBLOCK))
{
LOG_E("%s/%s: modbus_tcp_accept failed (%s)", __FILE__, __FUNCTION__, modbus_strerror(errno));
LOG_E("%s/%s: fcntl failed for socket (%s)", __FILE__, __FUNCTION__, strerror(errno));
goto server_exit;
}

LOG_I("Accept Modbus TCP connection ...");
while (*((gboolean *)run))
{
// Attempt to accept a client connection (non-blocking)
if (0 < modbus_tcp_accept(srv_ctx, &s))
{
break;
}

// Sleep briefly to avoid busy-waiting
usleep(200000); // Sleep for 200 ms
}

LOG_I("Allocate mapping ...");
mb_mapping = modbus_mapping_new(1, 0, 0, 0);
if (NULL == mb_mapping)
Expand Down Expand Up @@ -151,4 +166,5 @@ void modbus_server_stop()
pthread_join(modbus_server_thread_id, NULL);
}
modbus_server_thread_id = -1;
srv_ctx = NULL;
}
2 changes: 1 addition & 1 deletion modbus_server.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2023, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
93 changes: 75 additions & 18 deletions modbusacap.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2023, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,12 +30,14 @@ enum Mode
CLIENT = 1
};

static GMainLoop *main_loop = NULL;
static AXEventHandler *ehandler;
static AXParameter *axparameter = NULL;
static gboolean initialized = FALSE;
static guint8 mode = 0;
static guint32 port = 0;
static guint subscription;
static pthread_mutex_t lock;

static void open_syslog(const char *app_name)
{
Expand All @@ -44,7 +46,8 @@ static void open_syslog(const char *app_name)

static void close_syslog(void)
{
LOG_I("Exiting!");
LOG_I("%s/%s: Exiting!", __FILE__, __FUNCTION__);
closelog();
}

static void event_callback(guint subscription, AXEvent *event, void *data)
Expand Down Expand Up @@ -207,6 +210,22 @@ static void scenario_callback(const gchar *name, const gchar *value, void *data)
setup_event_subscription(&subscription, scenario);
}

static void close_current_modbus(const guint8 m)
{
switch (m)
{
case SERVER:
modbus_server_stop();
break;
case CLIENT:
modbus_client_cleanup();
break;
default:
LOG_E("%s/%s: %u is not a known mode", __FILE__, __FUNCTION__, mode);
break;
}
}

static void server_callback(const gchar *name, const gchar *value, void *data)
{
(void)data;
Expand All @@ -216,13 +235,16 @@ static void server_callback(const gchar *name, const gchar *value, void *data)
return;
}

pthread_mutex_lock(&lock);
close_current_modbus(mode);
LOG_I("%s/%s: Got new %s (%s)", __FILE__, __FUNCTION__, name, value);

// Setup Modbus for this mode
if (initialized && !setup_modbus(mode, port, value))
{
LOG_I("%s/%s: Failed to setup Modbus", __FILE__, __FUNCTION__);
}
pthread_mutex_unlock(&lock);
}

static void mode_callback(const gchar *name, const gchar *value, void *data)
Expand All @@ -234,6 +256,8 @@ static void mode_callback(const gchar *name, const gchar *value, void *data)
return;
}

pthread_mutex_lock(&lock);
close_current_modbus(mode);
mode = atoi(value);
assert(0 == mode || 1 == mode);
LOG_I("%s/%s: Got new %s (%s)", __FILE__, __FUNCTION__, name, mode == SERVER ? "server" : "client");
Expand All @@ -244,6 +268,7 @@ static void mode_callback(const gchar *name, const gchar *value, void *data)
LOG_I("%s/%s: Failed to setup Modbus", __FILE__, __FUNCTION__);
assert(FALSE);
}
pthread_mutex_unlock(&lock);
}

static void port_callback(const gchar *name, const gchar *value, void *data)
Expand All @@ -255,6 +280,8 @@ static void port_callback(const gchar *name, const gchar *value, void *data)
return;
}

pthread_mutex_lock(&lock);
close_current_modbus(mode);
port = atoi(value);
assert(1024 <= port || 65535 >= port);
LOG_I("%s/%s: Got new %s (%u)", __FILE__, __FUNCTION__, name, port);
Expand All @@ -265,6 +292,7 @@ static void port_callback(const gchar *name, const gchar *value, void *data)
LOG_I("%s/%s: Failed to setup Modbus", __FILE__, __FUNCTION__);
assert(FALSE);
}
pthread_mutex_unlock(&lock);
}

static gboolean setup_param(const gchar *name, AXParameterCallback callbackfn)
Expand Down Expand Up @@ -298,27 +326,54 @@ static gboolean setup_param(const gchar *name, AXParameterCallback callbackfn)
return TRUE;
}

static void init_signals(void)
static void signal_handler(gint signal_num)
{
switch (signal_num)
{
case SIGTERM:
case SIGABRT:
case SIGINT:
g_main_loop_quit(main_loop);
break;
default:
break;
}
}

static gboolean signal_handler_init(void)
{
struct sigaction sa;
struct sigaction sa = {0};

if (-1 == sigemptyset(&sa.sa_mask))
{
LOG_E("%s/%s: Failed to initialize signal handler: %s", __FILE__, __FUNCTION__, strerror(errno));
return FALSE;
}

sa.sa_handler = signal_handler;

if (0 > sigaction(SIGTERM, &sa, NULL) || 0 > sigaction(SIGABRT, &sa, NULL) || 0 > sigaction(SIGINT, &sa, NULL))
{
LOG_E("%s/%s: Failed to install signal handler: %s", __FILE__, __FUNCTION__, strerror(errno));
return FALSE;
}

sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGPIPE);
sa.sa_flags = 0;
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
return TRUE;
}

int main(int argc, char **argv)
{
GError *error = NULL;
int ret = 0;

init_signals();

char *app_name = basename(argv[0]);
open_syslog(app_name);

int ret = EXIT_SUCCESS;
if (!signal_handler_init())
{
ret = EXIT_FAILURE;
goto exit_syslog;
}

// Create event handler
ehandler = ax_event_handler_new();

Expand All @@ -328,7 +383,7 @@ int main(int argc, char **argv)
{
LOG_E("%s/%s: ax_parameter_new failed (%s)", __FILE__, __FUNCTION__, error->message);
g_error_free(error);
ret = 1;
ret = EXIT_FAILURE;
goto exit_ehandler;
}
// clang-format off
Expand All @@ -338,7 +393,7 @@ int main(int argc, char **argv)
!setup_param("Server", server_callback))
// clang-format on
{
ret = 1;
ret = EXIT_FAILURE;
goto exit_param;
}

Expand All @@ -348,10 +403,12 @@ int main(int argc, char **argv)

// Main loop
LOG_I("%s/%s: Ready", __FILE__, __FUNCTION__);
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
main_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(main_loop);

// Cleanup and controlled shutdown
LOG_I("%s/%s: Unreference main loop ...", __FILE__, __FUNCTION__);
g_main_loop_unref(main_loop);
exit_param:
LOG_I("%s/%s: Free parameter handler ...", __FILE__, __FUNCTION__);
ax_parameter_free(axparameter);
Expand All @@ -363,7 +420,7 @@ int main(int argc, char **argv)
// Cleanup Modbus
modbus_client_cleanup();
modbus_server_stop();

exit_syslog:
LOG_I("%s/%s: Closing syslog ...", __FILE__, __FUNCTION__);
close_syslog();

Expand Down
2 changes: 1 addition & 1 deletion modbusacap_common.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2023, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down

0 comments on commit ef335dc

Please sign in to comment.