EVP Agent SDK

Note

Embedding API interfaces are defined in include/evp/agent.h.

Although this project provides a stand-alone EVP Agent executable evp-agent, it can be embedded in a user application with EVP Agent SDK library libevp-agent.

Runtime

Embedding the agent in user application requires the following, as a minimum:

  1. Initialize an agent instance with evp_agent_setup(), which returns a instance of the agent context that is then required by all evp_agent_*() functions.

  2. Startup the agent with evp_agent_start().

  3. Call evp_agent_loop() within a loop that the user may break as they require or when the function returns an error. If previously connected see #3.

  4. Stop the agent with evp_agent_stop().

  5. Free resources with evp_agent_free().

  6. Then the application can safely exit.

int
main(int argc, char *argv[])
{
    struct evp_agent_context *agent = evp_agent_setup(argv[0]);

    int ret = evp_agent_start(agent);
    if (ret)
        goto release;

    while (ret == 0) {
        ret = evp_agent_loop(agent);
    }

    evp_agent_stop(agent);
release:
    evp_agent_free(agent);
    return 0;
}
struct evp_agent_context

Agent context object.

This contains the runtime data for the instance of the running agent.

struct evp_agent_context *evp_agent_setup(const char *progname)

Initialize an agent instance context object.

This must be called to create an agent context to pass to any evp_agent_*() functions that require evp_agent_context as parameter.

It takes a c string as parameter to set the program name to be printed in error basic log functions xerr() and xerrx().

The agent Status is expected to remain EVP_AGENT_STATUS_INIT at this stage.

Parameters:
  • progname – Program name

Returns:

A evp_agent_context object pointer.

Errors:

The function guaranties to return a valid allocated pointer, or abort if object memory allocation failed.

Example:

int
main(int argc, char *argv[])
{
    struct evp_agent_context *ctxt = evp_agent_setup(argv[0]);
    ...
}
int evp_agent_start(struct evp_agent_context *ctxt)

Start the agent.

Runs all initialization procedures to start up the agent.

The agent evp_agent_status is expected to pass to EVP_AGENT_STATUS_READY when startup is complete.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

Always return 0.

Errors:

This function may abort the program if TLS cannot be initialised or for other internal critical failure reasons.

int evp_agent_loop(struct evp_agent_context *ctxt)

This is the agent runtime routine to keep calling in an infinite loop.

Note

This is expected to block until agent events are emitted for processing.

It is therefore necessary to keep this call in the loop without any other blocking or time consuming calls, to avoid any unwanted hangs. or delays.

It is expected to handle the following status transitions:

Warning

If routine is called after a successful stop (evp_agent_stop()) the function will return 0 and no events will be processed.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

Returns 0 in mominal case or non-zero if an error occurred.

Errors:

If the agent has not been started with the call to evp_agent_start() (EVP_AGENT_STATUS_INIT), the function will return -1 and not process any event.

Example:

int ret = 0;
while (ret == 0) {
    ret = evp_agent_loop(ctxt);
}
int evp_agent_stop(struct evp_agent_context *ctxt)

Proceed to stopping the agent.

Shuts down all running instances, and free internal resources.

The agent Status is expected to pass to EVP_AGENT_STATUS_STOP when shudown is complete.

Note

Currently, the implementation does not support evp_agent_start() to be called again after a successful stop.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

Always return 0.

int evp_agent_free(struct evp_agent_context *ctxt)

Free allocated agent context and related resources.

This must be called only after evp_agent_stop() had been called.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Network Connection

The network connection can be controlled by the user.

Calling evp_agent_connect() will initiate connection to the network (Hub and Web). Agent can then be disconnected with evp_agent_disconnect().

The user may control connection as they see fit, as long as it called after evp_agent_start() and before evp_agent_stop().

It is standard to initiate connection after agent startup, and disconnect before stopping:

int
main(int argc, char *argv[])
{
    struct evp_agent_context *agent = evp_agent_setup(argv[0]);

    int ret = evp_agent_start(agent);
    if (ret)
        goto release;

    ret = evp_agent_connect(agent);
    if (ret)
        goto stop;

    while (ret == 0) {
        ret = evp_agent_loop(agent);
    }

    evp_agent_disconnect(agent);
stop:
    evp_agent_stop(agent);
release:
    evp_agent_free(agent);
    return 0;
}
int evp_agent_connect(struct evp_agent_context *ctxt)

Connect the agent to the Hub.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

Returns zero in case of success or non-zero in case of error.

int evp_agent_disconnect(struct evp_agent_context *ctxt)

Disconnect the agent from the Hub.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

Returns zero in case of success or non-zero in case of error.

Status

It is possible to query the agent status with evp_agent_get_status(), to handle different use cases according to the agent state.

See evp_agent_status for reference.

As a convenience, evp_agent_ready() queries the readiness of the agent after startup, It returns true if agent is not in EVP_AGENT_STATUS_INIT state.

enum evp_agent_status
Values:
enumerator EVP_AGENT_STATUS_INIT

agent has been created but not started

enumerator EVP_AGENT_STATUS_READY

agent is initialised but not connected

enumerator EVP_AGENT_STATUS_CONNECTING

agent is waiting for CONNACK

enumerator EVP_AGENT_STATUS_CONNECTED

agent is connected to hub

enumerator EVP_AGENT_STATUS_DISCONNECTING

agent is waiting for network operations to finish

enumerator EVP_AGENT_STATUS_DISCONNECTED

agent is disconnected from network

enumerator EVP_AGENT_STATUS_STOPPED

agent has been stopped

enum evp_agent_status evp_agent_get_status(struct evp_agent_context *ctxt)

Query the current state of the agent.

bool evp_agent_ready(struct evp_agent_context *ctxt)

Query the readiness of the agent after startup.

Parameters:
  • ctxt – Agent context pointer object

Returns:

true if agent is not in EVP_AGENT_STATUS_INIT state.

Platform

Platform allows user to redefine methods that may need specific implementation according to the platform the agent is running on.

A constant evp_agent_platform object may be declared and initialized with the user implemeted methods, otherwise standard POSIX platform methods will be used.

A method can be overloaded by defining it in the user code and initializing the platform object with the user method. All pointers left NULL will not overwrite default methods.

Then the overloaded platform must be registered with evp_agent_platform_register() to set the platform methods.

See Platform documentation.

int evp_agent_platform_register(struct evp_agent_context *ctxt,
                const struct evp_agent_platform *p);

Note

This method must be called at most once before evp_agent_start().

Example:

void
my_out_of_memory(const char *file, int line, const char *where, size_t siz)
{
    ...
}

void *
my_malloc(size_t sz)
{
    ...
}

void
my_free(void *p)
{
    ...
}

static struct evp_agent_platform my_platform = {
    .out_of_memory = my_out_of_memory,
    .secure_malloc = my_malloc,
    .secure_free = my_free,
};

int main(const int c, const char *argv[])
{
    struct evp_agent_context *agent = evp_agent_setup(argv[0]);
    evp_agent_platform_register(agent, &my_platform);

    ...

    return 0;
}
struct evp_agent_platform
int evp_agent_platform_register(struct evp_agent_context *ctxt, const struct evp_agent_platform *p)

Set the platform methods.

This method can be called only before evp_agent_start().

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

  • p – pointer to platform methods.

Returns:

Returns zero on success, non-zero otherwise.

Notification

The embedded API provides a way to be notified upon some internal events.

The event to watch must be register with evp_agent_notification_subscribe().

See Notifications documentation.

Example:

int on_reconcile_status(const void *args, void *user_data)
{
    struct reconcileStatusNotify *notify_value = args;

    printf("Reconcile status of %s is %s", notify_value->deploymentId,
           notify_value->reconcileStatus);
    return 0;
}

int main(const int c, const char *argv[])
{
    struct evp_agent_context *agent = evp_agent_setup(argv[0]);
    evp_agent_notification_subscribe(agent, "deployment/reconcileStatus",
                                     NULL);

    ...

    return 0;
}
int evp_agent_notification_subscribe(struct evp_agent_context *ctxt, const char *event, int (*cb)(const void *args, void *user_data), void *user_data)

Subscribes to an event using a custom callback.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

  • event – Human-readable string that uniquely identifies the event. It is recommended that event categories are defined similarly to directories i.e., using the forward slash ‘/’ character. For example: “event-group/event” .

  • cb – User-defined callback to attach to a given event. Several callbacks can be attached to a single event by calling this function repeatedly.

  • user_data – Opaque pointer to user-defined data. The library shall make no attempts to dereference this pointer, and it can be safely assigned to a null pointer.

Returns:

Returns zero on success, non-zero otherwise.

Warning

ctxt is currently ignored by this API but a valid pointer must be assigned for future compatibility.

Note

This function will create a deep copy of all of its arguments.

Note

This function is called under the EVP Agent context. In order to ensure the stability of the EVP agent, it is recommended that long-running or blocking user-defined tasks are either avoided or moved to a separate thread.

int evp_agent_notification_publish(struct evp_agent_context *ctxt, const char *event, const void *args)

Triggers a specific event that will call its associated callbacks.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

  • event – Human-readable string that uniquely identifies the event.

  • args – Event-specific arguments. The callback is then responsible to cast this data to the appropriate type.

Returns:

Returns zero on success, non-zero otherwise.

Deployment

Individual instances can be stopped with evp_agent_stop_instance(). There might be little need in production but is used for testing.

User may request to undeploy all instances and modules (for example, before a device reboot). This can be achieved with evp_agent_undeploy_all().

int evp_agent_stop_instance(struct evp_agent_context *ctxt, const char *name)

Stop a running instance.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

  • name – Instance name to stop.

Returns:

Returns zero in case of success or non-zero in case of error.

int evp_agent_undeploy_all(struct evp_agent_context *ctxt)

Undeploy instances.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

Returns zero in case of success or non-zero in case of error.

int evp_agent_empty_deployment_has_completed(struct evp_agent_context *ctxt)

Checks whether the deployment reconciliation loop has settled on an empty deployment.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

Returns zero in case of success or non-zero in case of error.

int evp_agent_request_pause_deployment(struct evp_agent_context *ctxt)

Pause agent deployment capability.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

zero on success

Returns:

EAGAIN if deployment cannot be paused yet due to a running operation. User needs to poll again to check that deployment has been successfully paused to guaranty no deployment is in progress.

Note

User can subscribe to deployment/reconcileStatus to be notified when deployment has been successfully paused.

int evp_agent_resume_deployment(struct evp_agent_context *ctxt)

Resume agent deployment capability.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

Returns:

zero

Messaging

It is possible to inject messages to the agent, like the Hub would, through MQTT requests, with evp_agent_send(). Even if this has little usage in production, it is very useful for testing.

Individual instances can be stopped with evp_agent_stop_instance(). There might be little need for this in production but is useful for testing.

User may request to undeploy all instances and modules (for example, before a device reboot). This can be achieved with evp_agent_undeploy_all().

int evp_agent_send(struct evp_agent_context *ctxt, const char *topic, const char *payload)

Send a message to the agent.

Parameters:
  • ctxt – Opaque pointer to internal agent-related data.

  • topic – Topic string to send.

  • payload – Payload string to send.

Returns:

Returns zero in case of success or non-zero in case of error.

Modules

This section presents functions related to module state.

const char *evp_agent_module_get_id(const struct module *module)

Returns the moduleId of a module

Parameters:
  • module – Opaque pointer to a module object.

Returns:

Returns the ID of the module.

int evp_agent_module_set_failure_msg(struct module *module, const char *fmt, ...)

Sets the failureMessage of a module

Parameters:
  • module – Opaque pointer to a module object.

bool evp_agent_module_is_in_use(const char *moduleId)

Checks if the module is loaded by the agent (in use)

Parameters:
  • moduleId – the ID of the module to be checked.

Returns:

Returns true if module is loaded by the Agent

void evp_agent_module_clear_failure_msg(struct module *module)

Clears the failureMessage of a module

Parameters:
  • module – Opaque pointer to a module object.

Thread safety

All evp_agent_*() functions are mutex protected, therefore thread-safe.