Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ name: CI
# something a shared workflow should know about) and its outputs feed
# build-test.yml's run-* inputs directly, preserving the path-based
# skip-on-docs-only-PR behavior. `broker-smoke` (host CLI tools against a
# real Mosquitto), `net-smoke` (cross-built mqtt_pub under Copperline's
# HostSocket board against a real Mosquitto - see tests/net/README.md),
# real Mosquitto), `broker-tls-smoke` (same, over a Mosquitto TLS listener
# - gated by the same `broker` filter), `net-smoke` (cross-built mqtt_pub
# under Copperline's HostSocket board against a real Mosquitto - see
# tests/net/README.md),
# `library-smoke` (mqtt.library opened and driven on-target - the plain
# OpenLibrary/CloseLibrary check, the full API against a real Mosquitto, and
# the mco_AutoReconnect backoff/auto-resubscribe check across a broker
Expand Down Expand Up @@ -92,6 +94,18 @@ jobs:
- name: Run broker smoke test
run: make broker-smoke

broker-tls-smoke:
name: Broker TLS smoke (host CLIs vs Mosquitto, TLS listener)
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.broker == 'true'
steps:
- uses: actions/checkout@v7
- name: Install Mosquitto
run: sudo apt-get update && sudo apt-get install -y mosquitto mosquitto-clients
- name: Run broker TLS smoke test
run: make broker-tls-smoke

net-smoke:
name: Net smoke (on-target mqtt_pub vs Mosquitto, Copperline HostSocket)
runs-on: ubuntu-latest
Expand All @@ -101,7 +115,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Install Mosquitto
run: apt-get update && apt-get install -y mosquitto
run: apt-get update && apt-get install -y mosquitto libssl-dev pkg-config
- name: Cross-build mqtt_pub, mqtt.library, and the host observer
run: PATH=/opt/amiga/bin:$PATH make m68k library cli
- name: Run net smoke test
Expand All @@ -116,7 +130,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Install Mosquitto
run: apt-get update && apt-get install -y mosquitto
run: apt-get update && apt-get install -y mosquitto libssl-dev pkg-config
- name: Run mqtt.library open/close smoke test
run: PATH=/opt/amiga/bin:$PATH make library-smoke
- name: Run mqtt.library end-to-end API smoke test
Expand Down
41 changes: 37 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# make test build and run the host-side unit tests (default)
# make cli build the host-native mqtt_pub/mqtt_sub tools
# make broker-smoke run the host tools against a local Mosquitto
# make broker-tls-smoke run the host tools' TLS support against a local Mosquitto
# make m68k cross-build the Amiga binaries (needs amiga-gcc on PATH)
# make m68k-docker cross-build inside the CI container (no local toolchain)
# make net-smoke on-target network test (Copperline HostSocket; no ROM/WB needed)
Expand All @@ -23,6 +24,34 @@ CC ?= cc
CFLAGS ?= -std=c99 -O2 -Wall -Wextra
CObjINC := -Isrc/core

# Host TLS transport (src/host/transport_openssl.c) links host OpenSSL
# 1.1+/3.x. Not on the default compiler search path on macOS (Homebrew
# keeps it out of /usr/include and /usr/lib to avoid shadowing the system
# LibreSSL), so go through pkg-config; fall back to plain -lssl -lcrypto for
# hosts where OpenSSL is where the compiler already expects it.
HOST_SSL_CFLAGS ?= $(shell pkg-config --cflags openssl 2>/dev/null)
HOST_SSL_LIBS ?= $(shell pkg-config --libs openssl 2>/dev/null || echo -lssl -lcrypto)

# Feature-detect OpenSSL rather than requiring it unconditionally: the
# shared amiga-dev CI image (ci/test-host, ci/build - see
# .github/workflows/ci.yml) has no OpenSSL headers and this repo has no way
# to add an apt-get step to those reusable-workflow jobs, so `cli`/`test`
# must still build cleanly there, just without TLS support in that build.
# Same idiom AmiSSL will use for the m68k side (see issue #3's plan) -
# amiauth's own `make diff` target follows the sibling precedent of keeping
# an optional host OpenSSL dependency out of its core build/test verbs
# entirely; this goes one step further and degrades gracefully instead,
# since -s/-S need to be real flags on the shipped mqtt_pub-host/
# mqtt_sub-host binaries (broker-tls-smoke's job installs libssl-dev, or
# gets it for free on a stock ubuntu-latest runner, so it always gets full
# TLS support).
HOST_HAS_TLS := $(shell $(CC) $(HOST_SSL_CFLAGS) -c -include openssl/ssl.h -x c /dev/null -o /dev/null >/dev/null 2>&1 && echo 1)
ifeq ($(HOST_HAS_TLS),1)
HOST_TLS_SRCS := src/host/transport_openssl.c
HOST_TLS_CFLAGS := $(HOST_SSL_CFLAGS) -DMIDGE_HOST_TLS
HOST_TLS_LIBS := $(HOST_SSL_LIBS)
endif

# --- m68k cross toolchain (Amiga build) ---
# Baseline per the project spec is 68020 (not 68000 - see CLAUDE.md).
M68K_CC ?= m68k-amigaos-gcc
Expand All @@ -49,7 +78,7 @@ DOCKER_USER := --user "$(shell id -u):$(shell id -g)"

CORE_SRCS := $(wildcard src/core/*.c)
TOOLS_SRCS := $(wildcard src/tools/*.c)
HOST_SRCS := src/host/transport_bsd.c src/host/args.c src/host/clock.c
HOST_SRCS := src/host/transport_bsd.c $(HOST_TLS_SRCS) src/host/args.c src/host/clock.c
AMIGA_SRCS := src/amiga/transport_bsdsocket.c src/amiga/args.c src/amiga/clock.c
LIB_SRCS := $(wildcard src/library/*.c)
TEST_SRCS := $(wildcard tests/test_*.c)
Expand Down Expand Up @@ -77,7 +106,7 @@ LIB_SFD := src/library/mqtt_lib.sfd
LIB_INCDIR := $(BUILD)/include
LIB_GENDIR := $(BUILD)/library-gen

.PHONY: all test cli broker-smoke m68k m68k-docker codec-selftest-m68k codec-selftest-m68k-docker net-smoke volamos-smoke volamos-test-target guide api-reference dist clean build test-host test-target lint library-headers library libsmoke-m68k library-smoke volamos-library-smoke libnet-m68k library-net-smoke volamos-library-net-smoke libreconn-m68k library-reconnect-smoke examples example-smoke
.PHONY: all test cli broker-smoke broker-tls-smoke m68k m68k-docker codec-selftest-m68k codec-selftest-m68k-docker net-smoke volamos-smoke volamos-test-target guide api-reference dist clean build test-host test-target lint library-headers library libsmoke-m68k library-smoke volamos-library-smoke libnet-m68k library-net-smoke volamos-library-net-smoke libreconn-m68k library-reconnect-smoke examples example-smoke

all: test cli

Expand Down Expand Up @@ -113,16 +142,20 @@ $(BUILD)/run-tests: $(CORE_SRCS) $(TEST_SRCS) $(CORE_HDRS) $(TEST_HDRS) | $(BUIL
cli: $(BUILD)/mqtt_pub-host $(BUILD)/mqtt_sub-host

$(BUILD)/mqtt_pub-host: $(CORE_SRCS) $(TOOLS_SRCS) $(HOST_SRCS) src/host/pub_main.c $(CORE_HDRS) $(TOOLS_HDRS) $(HOST_HDRS) | $(BUILD)/.dir
$(CC) $(CFLAGS) $(CObjINC) -Isrc/tools -Isrc/host $(CORE_SRCS) $(TOOLS_SRCS) $(HOST_SRCS) src/host/pub_main.c -o $@
$(CC) $(CFLAGS) $(CObjINC) -Isrc/tools -Isrc/host $(HOST_TLS_CFLAGS) $(CORE_SRCS) $(TOOLS_SRCS) $(HOST_SRCS) src/host/pub_main.c -o $@ $(HOST_TLS_LIBS)

$(BUILD)/mqtt_sub-host: $(CORE_SRCS) $(TOOLS_SRCS) $(HOST_SRCS) src/host/sub_main.c $(CORE_HDRS) $(TOOLS_HDRS) $(HOST_HDRS) | $(BUILD)/.dir
$(CC) $(CFLAGS) $(CObjINC) -Isrc/tools -Isrc/host $(CORE_SRCS) $(TOOLS_SRCS) $(HOST_SRCS) src/host/sub_main.c -o $@
$(CC) $(CFLAGS) $(CObjINC) -Isrc/tools -Isrc/host $(HOST_TLS_CFLAGS) $(CORE_SRCS) $(TOOLS_SRCS) $(HOST_SRCS) src/host/sub_main.c -o $@ $(HOST_TLS_LIBS)

# --- Host: end-to-end smoke test against a local Mosquitto ---
broker-smoke: cli
MQTT_PUB=$(BUILD)/mqtt_pub-host MQTT_SUB=$(BUILD)/mqtt_sub-host \
sh tests/broker/smoke.sh

broker-tls-smoke: cli
MQTT_PUB=$(BUILD)/mqtt_pub-host MQTT_SUB=$(BUILD)/mqtt_sub-host \
sh tests/broker/tls-smoke.sh

# --- m68k: Amiga binaries (amiga-gcc on PATH) ---
# Four binaries: mqtt_pub/mqtt_sub are the default, library-linked tools
# (OpenLibrary("mqtt.library") + the MQTT_* API, see src/amiga/pub_main_lib.c
Expand Down
23 changes: 18 additions & 5 deletions src/amiga/args.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@

#define PUB_TEMPLATE \
"HOST/A,PORT/N/K,TOPIC/A,MESSAGE/K,FILE/K,QOS/N/K,CLIENTID/K,USER/K," \
"PASSWORD/K,KEEPALIVE/N/K,RETAIN/S,VERBOSE/S"
"PASSWORD/K,KEEPALIVE/N/K,RETAIN/S,VERBOSE/S,TLS/S,TLSINSECURE/S"
#define SUB_TEMPLATE \
"HOST/A,PORT/N/K,TOPIC/A,QOS/N/K,CLIENTID/K,USER/K,PASSWORD/K," \
"KEEPALIVE/N/K,COUNT/N/K,VERBOSE/S"
"KEEPALIVE/N/K,COUNT/N/K,VERBOSE/S,TLS/S,TLSINSECURE/S"

enum {
PUB_HOST, PUB_PORT, PUB_TOPIC, PUB_MESSAGE, PUB_FILE, PUB_QOS,
PUB_CLIENTID, PUB_USER, PUB_PASSWORD, PUB_KEEPALIVE, PUB_RETAIN,
PUB_VERBOSE, PUB_NARGS
PUB_VERBOSE, PUB_TLS, PUB_TLSINSECURE, PUB_NARGS
};
enum {
SUB_HOST, SUB_PORT, SUB_TOPIC, SUB_QOS, SUB_CLIENTID, SUB_USER,
SUB_PASSWORD, SUB_KEEPALIVE, SUB_COUNT, SUB_VERBOSE, SUB_NARGS
SUB_PASSWORD, SUB_KEEPALIVE, SUB_COUNT, SUB_VERBOSE, SUB_TLS,
SUB_TLSINSECURE, SUB_NARGS
};

static struct RDArgs *g_rdargs;
Expand All @@ -33,7 +34,8 @@ int amiga_parse_args(int is_pub, tool_opts *opts)
int i;

memset(opts, 0, sizeof(*opts));
opts->port = 1883;
/* opts->port stays 0 (memset) until resolved after parsing, once we
* know whether TLS/TLSINSECURE was given. */
opts->keepalive = 60;

for (i = 0; i < nargs; i++)
Expand Down Expand Up @@ -61,6 +63,10 @@ int amiga_parse_args(int is_pub, tool_opts *opts)
opts->keepalive = (uint16_t)*(LONG *)args[PUB_KEEPALIVE];
opts->retain = args[PUB_RETAIN] ? 1 : 0;
opts->verbose = args[PUB_VERBOSE] ? 1 : 0;
opts->tls = args[PUB_TLS] ? 1 : 0;
opts->tls_insecure = args[PUB_TLSINSECURE] ? 1 : 0;
if (opts->tls_insecure)
opts->tls = 1;

if (!opts->message && !opts->file) {
fprintf(stderr, "mqtt_pub: MESSAGE or FILE is required\n");
Expand Down Expand Up @@ -88,8 +94,15 @@ int amiga_parse_args(int is_pub, tool_opts *opts)
if (args[SUB_COUNT])
opts->count = (int)*(LONG *)args[SUB_COUNT];
opts->verbose = args[SUB_VERBOSE] ? 1 : 0;
opts->tls = args[SUB_TLS] ? 1 : 0;
opts->tls_insecure = args[SUB_TLSINSECURE] ? 1 : 0;
if (opts->tls_insecure)
opts->tls = 1;
}

if (opts->port == 0)
opts->port = opts->tls ? 8883 : 1883;

if (opts->qos > 1) {
fprintf(stderr, "midge: QoS 2 is not supported (see docs/PROTOCOL.md)\n");
amiga_args_cleanup();
Expand Down
17 changes: 12 additions & 5 deletions src/host/args.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
int host_parse_args(int argc, char **argv, int is_pub, tool_opts *opts)
{
int ch;
const char *optstring = is_pub ? "h:p:t:m:f:q:i:u:P:k:rv"
: "h:p:t:q:i:u:P:k:C:v";
const char *optstring = is_pub ? "h:p:t:m:f:q:i:u:P:k:rvsS"
: "h:p:t:q:i:u:P:k:C:vsS";

memset(opts, 0, sizeof(*opts));
opts->port = 1883;
/* opts->port stays 0 (memset) until resolved after the option loop,
* once we know whether -s/-S was given. */
opts->keepalive = 60;

optind = 1;
Expand All @@ -37,19 +38,25 @@ int host_parse_args(int argc, char **argv, int is_pub, tool_opts *opts)
case 'r': opts->retain = 1; break;
case 'C': opts->count = atoi(optarg); break;
case 'v': opts->verbose = 1; break;
case 's': opts->tls = 1; break;
case 'S': opts->tls = 1; opts->tls_insecure = 1; break;
default:
fprintf(stderr,
is_pub ? "usage: %s -h host [-p port] -t topic "
"(-m message | -f file) [-q qos] [-i id] "
"[-u user] [-P pass] [-k keepalive] [-r] [-v]\n"
"[-u user] [-P pass] [-k keepalive] [-r] [-s] "
"[-S] [-v]\n"
: "usage: %s -h host [-p port] -t topic "
"[-q qos] [-i id] [-u user] [-P pass] "
"[-k keepalive] [-C count] [-v]\n",
"[-k keepalive] [-C count] [-s] [-S] [-v]\n",
argv[0]);
return -1;
}
}

if (opts->port == 0)
opts->port = opts->tls ? 8883 : 1883;

if (!opts->host || !opts->topic) {
fprintf(stderr, "%s: -h and -t are required\n", argv[0]);
return -1;
Expand Down
27 changes: 26 additions & 1 deletion src/host/pub_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@
#include "args.h"
#include "tool_opts.h"
#include "transport_bsd.h"
#ifdef MIDGE_HOST_TLS
#include "transport_openssl.h"
#endif

int main(int argc, char **argv)
{
tool_opts opts;
mqtt_transport transport;
bsd_ctx ctx;
#ifdef MIDGE_HOST_TLS
openssl_ctx ossl_ctx;
#endif
int connected;

/* Sending on a connection the broker has already closed raises SIGPIPE,
* which kills the process by default; MSG_NOSIGNAL isn't portable
Expand All @@ -20,7 +27,25 @@ int main(int argc, char **argv)
if (host_parse_args(argc, argv, 1, &opts) != 0)
return 2;

if (transport_bsd_connect(&transport, &ctx, opts.host, opts.port) != 0) {
#ifdef MIDGE_HOST_TLS
if (opts.tls) {
connected = transport_openssl_connect(&transport, &ossl_ctx,
opts.host, opts.port,
opts.tls_insecure) == 0;
} else {
connected = transport_bsd_connect(&transport, &ctx, opts.host,
opts.port) == 0;
}
#else
if (opts.tls) {
fprintf(stderr,
"mqtt_pub: TLS not supported - this build has no OpenSSL\n");
return 1;
}
connected = transport_bsd_connect(&transport, &ctx, opts.host,
opts.port) == 0;
#endif
if (!connected) {
fprintf(stderr, "mqtt_pub: cannot connect to %s:%u\n", opts.host,
(unsigned)opts.port);
return 1;
Expand Down
27 changes: 26 additions & 1 deletion src/host/sub_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,45 @@
#include "args.h"
#include "tool_opts.h"
#include "transport_bsd.h"
#ifdef MIDGE_HOST_TLS
#include "transport_openssl.h"
#endif

int main(int argc, char **argv)
{
tool_opts opts;
mqtt_transport transport;
bsd_ctx ctx;
#ifdef MIDGE_HOST_TLS
openssl_ctx ossl_ctx;
#endif
int connected;

/* See pub_main.c's identical signal(SIGPIPE, SIG_IGN) comment. */
signal(SIGPIPE, SIG_IGN);

if (host_parse_args(argc, argv, 0, &opts) != 0)
return 2;

if (transport_bsd_connect(&transport, &ctx, opts.host, opts.port) != 0) {
#ifdef MIDGE_HOST_TLS
if (opts.tls) {
connected = transport_openssl_connect(&transport, &ossl_ctx,
opts.host, opts.port,
opts.tls_insecure) == 0;
} else {
connected = transport_bsd_connect(&transport, &ctx, opts.host,
opts.port) == 0;
}
#else
if (opts.tls) {
fprintf(stderr,
"mqtt_sub: TLS not supported - this build has no OpenSSL\n");
return 1;
}
connected = transport_bsd_connect(&transport, &ctx, opts.host,
opts.port) == 0;
#endif
if (!connected) {
fprintf(stderr, "mqtt_sub: cannot connect to %s:%u\n", opts.host,
(unsigned)opts.port);
return 1;
Expand Down
Loading