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
7 changes: 6 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@
}
],
"dictionaries": ["project"],
"ignorePaths": ["**/CMakeLists.txt", "**/*.nix", "**/iwyu_mappings.yml"]
"ignorePaths": [
"**/CMakeLists.txt",
"**/*.yml",
"**/*.nix",
"**/iwyu_mappings.yml"
]
}
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
include:
- name: build with GCC
check: packages-default
- name: static build
check: packages-ggl-sdk-static
- name: build with Clang
check: build-clang
- name: cross-build for arm32 Musl
Expand Down
14 changes: 13 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ if(PROJECT_IS_TOP_LEVEL)

option(ENABLE_WERROR "Compile warnings as errors")

option(BUILD_SAMPLES "Build sample components" ON)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

include(GNUInstallDirs)
Expand Down Expand Up @@ -173,11 +175,21 @@ target_compile_definitions(ggl-sdk PRIVATE _GNU_SOURCE)
target_include_directories(ggl-sdk PRIVATE include priv_include)
target_include_directories(ggl-sdk SYSTEM INTERFACE include)

target_compile_definitions(ggl-sdk PRIVATE "GGL_MODULE=(\"${ggl-sdk}\")")
target_compile_definitions(ggl-sdk PRIVATE "GGL_MODULE=(\"ggl-sdk\")")
string(TOUPPER "${GGL_LOG_LEVEL}" log_level)
set(choose_level "$<IF:$<BOOL:${log_level}>,${log_level},DEBUG>")
target_compile_definitions(ggl-sdk PUBLIC GGL_LOG_LEVEL=GGL_LOG_${choose_level})

if(PROJECT_IS_TOP_LEVEL)
install(TARGETS ggl-sdk)

if(BUILD_SAMPLES)
file(GLOB SAMPLE_DIRS CONFIGURE_DEPENDS "samples/*")
foreach(sample_dir ${SAMPLE_DIRS})
get_filename_component(sample_name ${sample_dir} NAME_WLE)
add_executable(sample_${sample_name} ${sample_dir}/main.c)
target_link_libraries(sample_${sample_name} PRIVATE ggl-sdk)
install(TARGETS sample_${sample_name})
endforeach()
endif()
endif()
5 changes: 4 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
./priv_include
./src
./.clang-tidy
./samples
];
};

Expand Down Expand Up @@ -65,6 +66,8 @@
meta = defaultMeta;
};

packages.ggl-sdk-static = { pkgsStatic }: pkgsStatic.ggl-sdk;

checks =
let
clangBuildDir = { pkgs, pkg-config, clang-tools, cmake, ... }:
Expand Down Expand Up @@ -95,7 +98,7 @@
PATH=${lib.makeBinPath
(with pkgs; [clangd-tidy clang-tools fd])}:$PATH
clangd-tidy -j$(nproc) -p ${clangBuildDir pkgs} --color=always \
$(fd . ${filteredSrc} -e c -e h) |\
$(fd . ${filteredSrc}/src -e c -e h) |\
sed 's|\.\.${filteredSrc}/||'
'';

Expand Down
81 changes: 44 additions & 37 deletions include/ggl/ipc/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,73 @@
#define GGL_IPC_CLIENT_H

#include <ggl/arena.h>
#include <ggl/attr.h>
#include <ggl/buffer.h>
#include <ggl/error.h>
#include <ggl/ipc/error.h>
#include <ggl/object.h>
#include <time.h> // IWYU pragma: keep
#include <stdint.h>

struct timespec;

#define GGL_IPC_SVCUID_STR_LEN (16)
// Connection APIs

GglError ggipc_call(
int conn,
GglBuffer operation,
GglBuffer service_model_type,
GglMap params,
GglArena *alloc,
GglObject *result,
GglIpcError *remote_err
);
/// Connect to the Greengrass Nucleus from a component.
/// Not thread-safe due to use of getenv.
GglError ggipc_connect(void);

GglError ggipc_get_config_str(
int conn, GglBufList key_path, GglBuffer *component_name, GglBuffer *value
);
/// Connect to a GG-IPC socket with a given SVCUID token.
GglError ggipc_connect_with_token(GglBuffer socket_path, GglBuffer auth_token);

GglError ggipc_get_config_obj(
int conn,
GglBufList key_path,
GglBuffer *component_name,
GglArena *alloc,
GglObject *value
);
// IPC calls

GglError ggipc_update_config(
int conn,
GglBufList key_path,
const struct timespec *timestamp,
GglObject value_to_merge
);
/// Publish a message to a local topic in JSON format
GglError ggipc_publish_to_topic_json(GglBuffer topic, GglObject payload);

/// Publish a message to a local topic in binary format
/// Uses an allocator to base64-encode a binary message.
/// base64 encoding will allocate 4 bytes for every 3 payload bytes.
/// Additionally, up to 128 bytes may be allocated for an error message.
GglError ggipc_publish_to_topic_binary(
int conn, GglBuffer topic, GglBuffer payload, GglArena alloc
);

GglError ggipc_publish_to_topic_obj(
int conn, GglBuffer topic, GglObject payload
GglBuffer topic, GglBuffer payload, GglArena alloc
);

/// Publish an MQTT message to AWS IoT Core on a topic
/// Uses an allocator to base64-encode a binary message.
/// base64 encoding will allocate 4 bytes for every 3 payload bytes.
/// Additionally, up to 128 bytes may be allocated for an error message.
GglError ggipc_publish_to_iot_core(
int conn,
GglBuffer topic_name,
GglBuffer payload,
uint8_t qos,
GglArena *alloc
GglBuffer topic_name, GglBuffer payload, uint8_t qos, GglArena alloc
);

typedef void (*GgIpcSubscribeToIotCoreCallback)(
GglBuffer topic, GglBuffer payload
);

/// Subscribe to MQTT messages from AWS IoT Core on a topic or topic filter
GglError ggipc_subscribe_to_iot_core(
GglBuffer topic_filter, uint8_t qos, GgIpcSubscribeToIotCoreCallback handler
) NONNULL(3);

/// Get a configuration value for a component on the core device
GglError ggipc_get_config(
GglBufList key_path,
const GglBuffer *component_name,
GglArena *alloc,
GglObject *value
);

/// Get a string-typed configuration value for a component on the core device
/// Alternative API to ggipc_get_config for string type values.
GglError ggipc_get_config_str(
GglBufList key_path, const GglBuffer *component_name, GglBuffer *value
);

/// Update a configuration value for this component on the core device
GglError ggipc_update_config(
GglBufList key_path,
const struct timespec *timestamp,
GglObject value_to_merge
);

#endif
48 changes: 48 additions & 0 deletions include/ggl/ipc/client_raw.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// aws-greengrass-sdk-lite - Lightweight AWS IoT Greengrass SDK
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

#ifndef GGL_IPC_CLIENT_RAW_H
#define GGL_IPC_CLIENT_RAW_H

#include <ggl/arena.h>
#include <ggl/attr.h>
#include <ggl/buffer.h>
#include <ggl/error.h>
#include <ggl/ipc/error.h>
#include <ggl/object.h>

#define GGL_IPC_SVCUID_STR_LEN (16)

/// Maximum number of eventstream streams. Limits subscriptions.
/// Max subscriptions is this minus 2.
/// Can be configured with `-D GGL_IPC_MAX_MSG_LEN=<N>`.
#ifndef GGL_IPC_MAX_STREAMS
#define GGL_IPC_MAX_STREAMS 16
#endif

GglError ggipc_call(
GglBuffer operation,
GglBuffer service_model_type,
GglMap params,
GglArena *alloc,
GglObject *result,
GglIpcError *remote_err
);

typedef GglError (*GgIpcSubscribeCallback)(
void *ctx, GglBuffer service_model_type, GglMap data
);

GglError ggipc_subscribe(
GglBuffer operation,
GglBuffer service_model_type,
GglMap params,
GgIpcSubscribeCallback on_response,
void *ctx,
GglArena *alloc,
GglObject *result,
GglIpcError *remote_err
) NONNULL(4);

#endif
1 change: 1 addition & 0 deletions misc/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ LOGE
LOGI
LOGT
LOGW
MQTT
NOLINTNEXTLINE
11 changes: 4 additions & 7 deletions priv_include/ggl/ipc/client_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#ifndef GGL_IPC_CLIENT_PRIV_H
#define GGL_IPC_CLIENT_PRIV_H

#include <ggl/attr.h>
#include <ggl/buffer.h>
#include <ggl/error.h>

Expand All @@ -23,12 +22,10 @@
/// Connect to GG-IPC server using component name.
/// If svcuid is non-null, it will be filled with the component's identity
/// token. Buffer must be able to hold at least GGL_IPC_SVCUID_STR_LEN.
GglError ggipc_connect_by_name(
GglBuffer socket_path, GglBuffer component_name, int *fd, GglBuffer *svcuid
) NONNULL(3);

GglError ggipc_private_get_system_config(
int conn, GglBuffer key, GglBuffer *value
GglError ggipc_connect_with_name(
GglBuffer socket_path, GglBuffer component_name, GglBuffer *svcuid
);

GglError ggipc_private_get_system_config(GglBuffer key, GglBuffer *value);

#endif
58 changes: 58 additions & 0 deletions samples/iot_core_mqtt/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Sample component demonstrating pubsub with AWS IoT Core

#include <ggl/arena.h>
#include <ggl/buffer.h>
#include <ggl/error.h>
#include <ggl/ipc/client.h>
#include <ggl/sdk.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

static void response_handler(GglBuffer topic, GglBuffer payload) {
printf(
"Received [%.*s] on [%.*s].\n",
(int) payload.len,
payload.data,
(int) topic.len,
topic.data
);
}

int main(void) {
setvbuf(stdout, NULL, _IONBF, 0);

ggl_sdk_init();

GglError ret = ggipc_connect();
if (ret != GGL_ERR_OK) {
fprintf(stderr, "Failed to connect to GG nucleus.\n");
exit(1);
}
printf("Connected to GG nucleus.\n");

ret = ggipc_subscribe_to_iot_core(GGL_STR("hello"), 0, &response_handler);
if (ret != GGL_ERR_OK) {
fprintf(stderr, "Failed to call subscribe_to_iot_core.\n");
exit(1);
}
printf("Subscribed to topic.\n");

while (true) {
static uint8_t publish_mem[5000];
GglArena publish_arena = ggl_arena_init(GGL_BUF(publish_mem));

ret = ggipc_publish_to_iot_core(
GGL_STR("hello"), GGL_STR("world"), 0, publish_arena
);
if (ret != GGL_ERR_OK) {
fprintf(stderr, "Failed to call publish_to_iot_core.\n");
exit(1);
}
printf("Published to topic.\n");

sleep(15);
}
}
29 changes: 29 additions & 0 deletions samples/iot_core_mqtt/recipe.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
RecipeFormatVersion: "2020-01-25"
ComponentName: "aws-greengrass-sdk-lite.samples.iot_core_mqtt"
ComponentVersion: "1.0.0"
ComponentType: "aws.greengrass.generic"
ComponentDescription:
Example component using aws-greengrass-sdk-lite to publish/subscribe to AWS
IoT Core
ComponentPublisher: AWS
ComponentConfiguration:
DefaultConfiguration:
accessControl:
aws.greengrass.ipc.mqttproxy:
access_policy:
policyDescription: "Allows pub/sub to IoT Core topic hello"
operations:
- aws.greengrass#PublishToIoTCore"
- aws.greengrass#SubscribeToIoTCore"
resources:
- "hello"
Manifests:
- Platform:
os: linux
runtime: "*"
Lifecycle:
run:
Script: "{artifacts:path}/sample_iot_core_mqtt"
Artifacts:
- URI: s3://EXAMPLE_BUCKET/sample_iot_core_mqtt
Loading