Skip to content

handle IPC call errors and bubble them up #831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 4, 2025
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
8 changes: 6 additions & 2 deletions bins/ggipcd/src/ipc_dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ static const size_t SERVICE_COUNT
= sizeof(SERVICE_TABLE) / sizeof(SERVICE_TABLE[0]);

GglError ggl_ipc_handle_operation(
GglBuffer operation, GglMap args, uint32_t handle, int32_t stream_id
GglBuffer operation,
GglMap args,
uint32_t handle,
int32_t stream_id,
GglIpcError *ipc_error
) {
for (size_t i = 0; i < SERVICE_COUNT; i++) {
const GglIpcService *service = SERVICE_TABLE[i];
Expand Down Expand Up @@ -71,7 +75,7 @@ GglError ggl_ipc_handle_operation(
GglBumpAlloc balloc = ggl_bump_alloc_init(GGL_BUF(resp_mem));

return service_op->handler(
&info, args, handle, stream_id, &balloc.alloc
&info, args, handle, stream_id, ipc_error, &balloc.alloc
);
}
}
Expand Down
7 changes: 6 additions & 1 deletion bins/ggipcd/src/ipc_dispatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
#ifndef GGL_IPC_DISPATCH_H
#define GGL_IPC_DISPATCH_H

#include "ipc_server.h"
#include <ggl/buffer.h>
#include <ggl/error.h>
#include <ggl/object.h>
#include <stdint.h>

GglError ggl_ipc_handle_operation(
GglBuffer operation, GglMap args, uint32_t handle, int32_t stream_id
GglBuffer operation,
GglMap args,
uint32_t handle,
int32_t stream_id,
GglIpcError *ipc_error
);

#endif
98 changes: 89 additions & 9 deletions bins/ggipcd/src/ipc_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,67 @@ static GglError release_client_subscriptions(uint32_t handle, size_t index) {
return ggl_ipc_release_subscriptions_for_conn(handle);
}

static void get_ipc_err_info(
GglIpcErrorCode error_code,
GglBuffer *err_str,
GglBuffer *service_model_type
) {
switch (error_code) {
case GGL_IPC_ERR_SERVICE_ERROR:
*err_str = GGL_STR("ServiceError");
*service_model_type = GGL_STR("aws.greengrass#ServiceError");
return;

case GGL_IPC_ERR_RESOURCE_NOT_FOUND:
*err_str = GGL_STR("ResourceNotFoundError");
*service_model_type = GGL_STR("aws.greengrass#ResourceNotFoundError");
return;

case GGL_IPC_ERR_INVALID_ARGUMENTS:
*err_str = GGL_STR("InvalidArgumentsError");
*service_model_type = GGL_STR("aws.greengrass#InvalidArgumentsError");
return;

case GGL_IPC_ERR_COMPONENT_NOT_FOUND:
*err_str = GGL_STR("ComponentNotFoundError");
*service_model_type = GGL_STR("aws.greengrass#ComponentNotFoundError");
return;

case GGL_IPC_ERR_UNAUTHORIZED_ERROR:
*err_str = GGL_STR("UnauthorizedError");
*service_model_type = GGL_STR("aws.greengrass#UnauthorizedError");
return;

case GGL_IPC_ERR_CONFLICT_ERROR:
*err_str = GGL_STR("ConflictError");
*service_model_type = GGL_STR("aws.greengrass#ConflictError");
return;

case GGL_IPC_ERR_FAILED_UPDATE_CONDITION_CHECK_ERROR:
*err_str = GGL_STR("FailedUpdateConditionCheckError");
*service_model_type
= GGL_STR("aws.greengrass#FailedUpdateConditionCheckError");
return;

case GGL_IPC_ERR_INVALID_TOKEN_ERROR:
*err_str = GGL_STR("InvalidTokenError");
*service_model_type = GGL_STR("aws.greengrass#InvalidTokenError");
return;

case GGL_IPC_ERR_INVALID_RECIPE_DIRECTORY_PATH_ERROR:
*err_str = GGL_STR("InvalidRecipeDirectoryPathError");
*service_model_type
= GGL_STR("aws.greengrass#InvalidRecipeDirectoryPathError");
return;

case GGL_IPC_ERR_INVALID_ARTIFACTS_DIRECTORY_PATH_ERROR:
*err_str = GGL_STR("InvalidArtifactsDirectoryPathError");
*service_model_type
= GGL_STR("aws.greengrass#InvalidArtifactsDirectoryPathError");
return;
}
}

static GglError deserialize_payload(GglBuffer payload, GglMap *out) {
GglObject obj;

Expand Down Expand Up @@ -253,26 +314,41 @@ static GglError handle_conn_init(
);
}

static GglError send_stream_error(uint32_t handle, int32_t stream_id) {
static GglError send_stream_error(
uint32_t handle, int32_t stream_id, GglIpcError ipc_error
) {
GGL_LOGE("Sending error on client %u stream %d.", handle, stream_id);

GGL_MTX_SCOPE_GUARD(&resp_array_mtx);
GglBuffer resp_buffer = GGL_BUF(resp_array);

GglBuffer service_model_type;
GglBuffer error_code;

get_ipc_err_info(ipc_error.error_code, &error_code, &service_model_type);

// TODO: Match classic error response
EventStreamHeader resp_headers[] = {
{ GGL_STR(":message-type"),
{ EVENTSTREAM_INT32, .int32 = EVENTSTREAM_APPLICATION_ERROR } },
{ GGL_STR(":message-flags"),
{ EVENTSTREAM_INT32, .int32 = EVENTSTREAM_TERMINATE_STREAM } },
{ GGL_STR(":stream-id"), { EVENTSTREAM_INT32, .int32 = stream_id } },

{ GGL_STR(":content-json"),
{ EVENTSTREAM_STRING, .string = GGL_STR("application/json") } },
{ GGL_STR("service-model-type"),
{ EVENTSTREAM_STRING, .string = service_model_type } },
};
const size_t RESP_HEADERS_LEN
= sizeof(resp_headers) / sizeof(resp_headers[0]);
size_t resp_headers_len = sizeof(resp_headers) / sizeof(resp_headers[0]);

GglError ret = eventstream_encode(
&resp_buffer, resp_headers, RESP_HEADERS_LEN, GGL_NULL_READER
&resp_buffer,
resp_headers,
resp_headers_len,
ggl_json_reader(&GGL_OBJ_MAP(GGL_MAP(
{ GGL_STR("_message"), GGL_OBJ_BUF(ipc_error.message) },
{ GGL_STR("_errorCode"), GGL_OBJ_BUF(error_code) }
)))
);
if (ret != GGL_ERR_OK) {
return ret;
Expand All @@ -284,7 +360,8 @@ static GglError send_stream_error(uint32_t handle, int32_t stream_id) {
static GglError handle_stream_operation(
uint32_t handle,
EventStreamMessage *msg,
EventStreamCommonHeaders common_headers
EventStreamCommonHeaders common_headers,
GglIpcError *ipc_error
) {
if (common_headers.message_type != EVENTSTREAM_APPLICATION_MESSAGE) {
GGL_LOGE("Client sent unhandled message type.");
Expand Down Expand Up @@ -326,7 +403,7 @@ static GglError handle_stream_operation(
}

return ggl_ipc_handle_operation(
operation, payload_data, handle, common_headers.stream_id
operation, payload_data, handle, common_headers.stream_id, ipc_error
);
}

Expand All @@ -340,13 +417,16 @@ static GglError handle_operation(
return GGL_ERR_INVALID;
}

GglError ret = handle_stream_operation(handle, msg, common_headers);
GglIpcError ipc_error = { 0 };

GglError ret
= handle_stream_operation(handle, msg, common_headers, &ipc_error);
if (ret == GGL_ERR_FATAL) {
return GGL_ERR_FAILURE;
}

if (ret != GGL_ERR_OK) {
return send_stream_error(handle, common_headers.stream_id);
return send_stream_error(handle, common_headers.stream_id, ipc_error);
}

return GGL_ERR_OK;
Expand Down
18 changes: 18 additions & 0 deletions bins/ggipcd/src/ipc_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@

#define GGL_IPC_PAYLOAD_MAX_SUBOBJECTS 50

typedef enum {
GGL_IPC_ERR_SERVICE_ERROR = 0,
GGL_IPC_ERR_RESOURCE_NOT_FOUND,
GGL_IPC_ERR_COMPONENT_NOT_FOUND,
GGL_IPC_ERR_INVALID_ARGUMENTS,
GGL_IPC_ERR_UNAUTHORIZED_ERROR,
GGL_IPC_ERR_CONFLICT_ERROR,
GGL_IPC_ERR_FAILED_UPDATE_CONDITION_CHECK_ERROR,
GGL_IPC_ERR_INVALID_TOKEN_ERROR,
GGL_IPC_ERR_INVALID_RECIPE_DIRECTORY_PATH_ERROR,
GGL_IPC_ERR_INVALID_ARTIFACTS_DIRECTORY_PATH_ERROR
} GglIpcErrorCode;

typedef struct {
GglIpcErrorCode error_code;
GglBuffer message;
} GglIpcError;

/// Start the GG-IPC server on a given socket
GglError ggl_ipc_listen(const char *socket_name, const char *socket_path);

Expand Down
2 changes: 2 additions & 0 deletions bins/ggipcd/src/ipc_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef GGL_IPC_SERVICE_H
#define GGL_IPC_SERVICE_H

#include "ipc_server.h"
#include <ggl/alloc.h>
#include <ggl/buffer.h>
#include <ggl/error.h>
Expand All @@ -22,6 +23,7 @@ typedef GglError GglIpcOperationHandler(
GglMap args,
uint32_t handle,
int32_t stream_id,
GglIpcError *ipc_error,
GglAlloc *alloc
);

Expand Down
39 changes: 29 additions & 10 deletions bins/ggipcd/src/services/authorizationagent/token_validator.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ GglError ggl_handle_token_validation(
GglMap args,
uint32_t handle,
int32_t stream_id,
GglIpcError *ipc_error,
GglAlloc *alloc
) {
(void) alloc;
Expand All @@ -28,33 +29,51 @@ GglError ggl_handle_token_validation(
)) {
GGL_LOGE(
"Component %.*s does not have access to token verification IPC "
"command",
"command.",
(int) info->component.len,
info->component.data
);

*ipc_error = (GglIpcError
) { .error_code = GGL_IPC_ERR_UNAUTHORIZED_ERROR,
.message = GGL_STR(
"Component does not have access to token verification IPC "
"command."
) };

return GGL_ERR_FAILURE;
}

GglObject *svcuid_obj;
GglError ret = ggl_map_validate(
args,
GGL_MAP_SCHEMA({ GGL_STR("token"), true, GGL_TYPE_BUF, &svcuid_obj }, )
);
if (ret != GGL_ERR_OK) {
GGL_LOGE("Received invalid paramters.");
GGL_LOGE("Received invalid parameters.");
*ipc_error = (GglIpcError
) { .error_code = GGL_IPC_ERR_SERVICE_ERROR,
.message = GGL_STR("Received invalid parameters.") };
return GGL_ERR_INVALID;
}

if (ipc_svcuid_exists(svcuid_obj->buf) == GGL_ERR_OK) {
return ggl_ipc_response_send(
handle,
stream_id,
GGL_STR("aws.greengrass#ValidateAuthorizationTokenResponse"),
GGL_OBJ_MAP(GGL_MAP({ GGL_STR("isValid"), GGL_OBJ_BOOL(true) }))
);
if (ipc_svcuid_exists(svcuid_obj->buf) != GGL_ERR_OK) {
*ipc_error = (GglIpcError
) { .error_code = GGL_IPC_ERR_INVALID_TOKEN_ERROR,
.message = GGL_STR(
"Invalid token used by stream manager when trying to authorize."
) };

// GreenGrass Java returns an error to the caller instead of setting the
// value to 'false'.
// https://github.com/aws-greengrass/aws-greengrass-nucleus/blob/b003cf0db575f546456bef69530126cf3e0b6a68/src/main/java/com/aws/greengrass/authorization/AuthorizationIPCAgent.java#L83
return GGL_ERR_FAILURE;
}

return ggl_ipc_response_send(
handle,
stream_id,
GGL_STR("aws.greengrass#ValidateAuthorizationTokenResponse"),
GGL_OBJ_MAP(GGL_MAP({ GGL_STR("isValid"), GGL_OBJ_BOOL(false) }))
GGL_OBJ_MAP(GGL_MAP({ GGL_STR("isValid"), GGL_OBJ_BOOL(true) }))
);
}
11 changes: 10 additions & 1 deletion bins/ggipcd/src/services/cli/create_local_deployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ GglError ggl_handle_create_local_deployment(
GglMap args,
uint32_t handle,
int32_t stream_id,
GglIpcError *ipc_error,
GglAlloc *alloc
) {
GGL_MAP_FOREACH(pair, args) {
Expand Down Expand Up @@ -53,6 +54,9 @@ GglError ggl_handle_create_local_deployment(
= ggl_ipc_auth(info, GGL_STR(""), ggl_ipc_default_policy_matcher);
if (ret != GGL_ERR_OK) {
GGL_LOGE("IPC Operation not authorized.");
*ipc_error = (GglIpcError
) { .error_code = GGL_IPC_ERR_SERVICE_ERROR,
.message = GGL_STR("IPC Operation not authorized.") };
return GGL_ERR_INVALID;
}

Expand All @@ -68,11 +72,16 @@ GglError ggl_handle_create_local_deployment(

if (ret != GGL_ERR_OK) {
GGL_LOGE("Failed to create local deployment.");
*ipc_error = (GglIpcError
) { .error_code = GGL_IPC_ERR_SERVICE_ERROR,
.message = GGL_STR("Failed to create local deployment.") };
return ret;
}

if (result.type != GGL_TYPE_BUF) {
GGL_LOGE("Response not a string.");
GGL_LOGE("Received deployment ID not a string.");
*ipc_error = (GglIpcError) { .error_code = GGL_IPC_ERR_SERVICE_ERROR,
.message = GGL_STR("Internal error.") };
return GGL_ERR_FAILURE;
}

Expand Down
Loading
Loading