COAP_PERSIST(3)

NAME
coap_persist, coap_persist_startup, coap_persist_stop, coap_persist_track_funcs, coap_persist_observe_add, coap_persist_set_observe_num - Work with CoAP persist support

SYNOPSIS

#include <coap3/coap.h>

int coap_persist_startup(coap_context_t *context, const char *dyn_resource_save_file, const char *observe_save_file, const char *obs_cnt_save_file, uint32_t save_freq);

void coap_persist_stop(coap_context_t *context);

void coap_persist_track_funcs(coap_context_t *context, coap_observe_added_t observe_added, coap_observe_deleted_t observe_deleted, coap_track_observe_value_t track_observe_value, coap_dyn_resource_added_t dyn_resource_added, coap_resource_deleted_t resource_deleted, uint32_t save_freq, void *user_data);

coap_subscription_t *coap_persist_observe_add(coap_context_t *context, coap_proto_t e_proto, const coap_address_t *e_listen_addr, const coap_addr_tuple_t *s_addr_info, const coap_bin_const_t *raw_packet, const coap_bin_const_t *oscore_info);

void coap_persist_set_observe_num(coap_resource_t *resource, uint32_t start_observe_no);

For specific (D)TLS library support, link with -lcoap-3-notls, -lcoap-3-gnutls, -lcoap-3-openssl, -lcoap-3-mbedtls, -lcoap-3-wolfssl or -lcoap-3-tinydtls. Otherwise, link with -lcoap-3 to get the default (D)TLS library support.

DESCRIPTION

When a coap-server is restarted, state information does not usually persist over the restart. libcoap has optional compiled in support for maintaining resources that were dynamically created, tracking ongoing observe subscriptions and maintaining OSCORE protection.

There are callbacks provided to support doing this as an alternative persist storage in the coap-server application.

NOTE: The observe persist support is only available for UDP sessions.

When using the libcoap compiled in support, only two functions need to be called by the application. coap_persist_startup() defines the file names to use for maintaining the persist information over an application restart, and coap_persist_stop() is called to preserve any persist information over the server restart.

FUNCTIONS

Function: coap_persist_startup()

The coap_persist_startup() function is used to enable persist tracking for context so when a coap-server is restarted, the persist tracked information can be added back in for the server logic. dyn_resource_save_file is used to save the current list of resources created from a request to the unknown resource. observe_save_file is used to save the current list of active observe subscriptions. obs_cnt_save_file is used to save the current observe counter used when sending an observe unsolicited response. obs_cnt_save_file only gets updated every save_freq updates.

If any of the files exist and are not empty, when coap_persist_startup() is called, the information is loaded back into the server logic, and for the active observe subscriptions a new server session is created for sending out the ongoing observe updates (UDP only supported).

If a file is defined as NULL, then that particular persist information is not tracked by the libcoap module. This allows a combination of coap_persist_track_funcs() for customized persist tracking followed by a call to coap_persist_startup().

Function: coap_persist_stop()

The coap_persist_stop() function is used to disable any current persist tracking as set up by coap_persist_startup() for context and preserve the tracking for when the coap-server application restarts.

If using coap_persist_track_funcs(), then calling coap_persist_stop() will stop any 4.04 unsolicited response messages being sent when a resource that has an active observe subscription is deleted (as happens when coap_free_context() is subsequentially called).

Function: coap_persist_track_funcs()

The coap_persist_track_funcs() function is used to setup callback functions associated with context that track information so that the current tracked information state can be rebuilt following a server application restart. It is the responsibility of the server application to track the appropriate information.

The observe_added callback function prototype, called when a client subscribes to a resource for observation, is defined as:

    /**
     * Callback handler definition called when a new observe has been set up,
     * as defined in coap_persist_track_funcs().
     *
     * @param session The current session.
     * @param observe_key The pointer to the subscription.
     * @param e_proto The CoAP protocol in use for the session / endpoint.
     * @param e_listen_addr The IP/port that the endpoint is listening on.
     * @param s_addr_info Local / Remote IP addresses. ports etc. of session.
     * @param raw_packet L7 packet as seen on the wire (could be concatenated if
     *                   Block1 FETCH is being used).
     * @param oscore_info Has OSCORE information if OSCORE is protecting the
     *                    session or NULL if OSCORE is not in use.
     * @param user_data Application provided information from
     *                  coap_persist_track_funcs().
     *
     * @return @c 1 if success else @c 0.
     */
    typedef int (*coap_observe_added_t)(coap_session_t *session,
                                        coap_subscription_t *observe_key,
                                        coap_proto_t e_proto,
                                        coap_address_t *e_listen_addr,
                                        coap_addr_tuple_t *s_addr_info,
                                        coap_bin_const_t *raw_packet,
                                        coap_bin_const_t *oscore_info,
                                        void *user_data);

The observe_deleted callback function prototype, called when a client removes the subscription to a resource for observation, is defined as:

    /**
     * Callback handler definition called when an observe is being removed,
     * as defined in coap_persist_track_funcs().
     *
     * @param session The current session.
     * @param observe_key The pointer to the subscription.
     * @param user_data Application provided information from
     *                  coap_persist_track_funcs().
     *
     * @return @c 1 if success else @c 0.
     */
    typedef int (*coap_observe_deleted_t)(coap_session_t *session,
                                          coap_subscription_t *observe_key,
                                          void *user_data);

The track_observe_value callback function prototype, called when a new unsolicited observe response is went (every save_freq), is defined as:

    /**
     * Callback handler definition called when an observe unsolicited response is
     * being sent, as defined in coap_persist_track_funcs().
     *
     * Note: This will only get called every save_freq as defined by
     * coap_persist_track_funcs().
     *
     * @param context The current CoAP context.
     * @param resource_name The uri path name of the resource.
     * @param observe_num The current observe value just sent.
     * @param user_data Application provided information from
     *                  coap_persist_track_funcs().
     *
     * @return @c 1 if success else @c 0.
     */
    typedef int (*coap_track_observe_value_t)(coap_context_t *context,
                                              coap_str_const_t *resource_name,
                                              uint32_t observe_num,
                                              void *user_data);

The dyn_resource_added callback function prototype, called whenever a resource is created from a request that is calling the resource unknown handler, is defined as:

    /**
     * Callback handler definition called when a dynamic resource is getting
     * created, as defined in coap_persist_track_funcs().
     *
     * @param session The current CoAP session.
     * @param resource_name The uri path name of the resource.
     * @param raw_packet L7 packet as seen on the wire (could be concatenated if
     *                   Block1 PUT/POST/FETCH used to create resource).
     * @param user_data Application provided information from
     *                  coap_persist_track_funcs().
     *
     * @return @c 1 if success else @c 0.
     */
    typedef int (*coap_dyn_resource_added_t)(coap_session_t *session,
                                             coap_str_const_t *resource_name,
                                             coap_bin_const_t *raw_packet,
                                             void *user_data);

The resource_deleted callback function prototype, called whenever a resource is deleted, is defined as:

    /**
     * Callback handler definition called when resource is removed,
     * as defined in coap_persist_track_funcs().
     *
     * This will remove any dynamic resources that are being tracked as well
     * as any observe value tracking.
     *
     * @param context The current CoAP context.
     * @param resource_name The uri path name of the resource.
     * @param user_data Application provided information from
     *                  coap_persist_track_funcs().
     *
     * @return @c 1 if success else @c 0.
     */
    typedef int (*coap_resource_deleted_t)(coap_context_t *context,
                                           coap_str_const_t *resource_name,
                                           void *user_data);

save_freq defines the frequency of the update to the observe value when libcoap calls track_observe_value. user_data is application defined and is passed into all of the callback handlers.

Function: coap_persist_observe_add()

The coap_persist_observe_add() function is used to set up a session and a observe subscription request (typically following a server reboot) so that a client can continue to receive unsolicited observe responses without having to establish a new session and issue a new observe subscription request. The new session is associated with the endpoint defined by e_proto and e_listen_address. The session has the IP addresses as defined by s_addr_info. raw_packet contains the layer 7 of the IP packet that was originally used to request the observe subscription. Optional oscore_info defines the OSCORE information if packets are protected by OSCORE. e_proto, e_listen_addr, s_addr_info, raw_packet and oscore_info are the same as passed into the coap_observe_added_t callback.

Function: coap_persist_set_observe_num()

The coap_persist_set_observe_num() function is used to update the resource's current observe counter to start from start_observe_no instead of 0,

RETURN VALUES

coap_persist_startup() returns 1 on success else 0.

coap_persist_observe_add() returns a newly created observe subscription or NULL on failure.

EXAMPLES

Simple Time Server

    #include <coap3/coap.h>
    #include <stdio.h>
    coap_resource_t *time_resource = NULL;
    /* specific GET "time" handler, called from hnd_get_generic() */
    static void
    hnd_get_time(coap_resource_t *resource, coap_session_t *session,
                 const coap_pdu_t *request, const coap_string_t *query,
                 coap_pdu_t *response) {
      unsigned char buf[40];
      size_t len;
      time_t now;
      (void)resource;
      (void)session;
      /* ... Additional analysis code for resource, request pdu etc.  ... */
      /* After analysis, generate a suitable response */
      /* Note that token, if set, is already in the response pdu */
      now = time(NULL);
      if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
        /* Output secs since Jan 1 1970 */
        len = snprintf((char *)buf, sizeof(buf), "%lu", now);
      } else {
        /* Output human-readable time */
        struct tm *tmp;
        tmp = gmtime(&now);
        if (!tmp) {
          /* If 'now' is not valid */
          coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
          return;
        }
        len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
      }
      coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
      /*
       * Invoke coap_add_data_large_response() to do all the hard work.
       *
       * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in
       * Define how long this response is valid for (secs) - 1 - to add in.
       * ETAG Option added internally with unique value as param set to 0
       *
       * OBSERVE Option added internally if needed within the function
       * BLOCK2 Option added internally if output too large
       * SIZE2 Option added internally
       */
      coap_add_data_large_response(resource, session, request, response,
                                   query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
                                   len,
                                   buf, NULL, NULL);
    }
    /* Generic GET handler */
    static void
    hnd_get_generic(coap_resource_t *resource, coap_session_t *session,
                    const coap_pdu_t *request, const coap_string_t *query,
                    coap_pdu_t *response) {
      coap_str_const_t *uri_path = coap_resource_get_uri_path(resource);
      if (!uri_path) {
        /* Unexpected Failure */
        coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
        return;
      }
      /* Is this the "time" resource" ? */
      if (coap_string_equal(uri_path, coap_make_str_const("time"))) {
        hnd_get_time(resource, session, request, query, response);
        return;
      }
      /* Other resources code */
      /* Failure response */
      coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
    }
    /* Initialize generic GET handler */
    static void
    init_resources(coap_context_t *ctx) {
      coap_resource_t *r;
      /* Create a resource to return return or update time */
      r = coap_resource_init(coap_make_str_const("time"),
                             COAP_RESOURCE_FLAGS_NOTIFY_CON);
      /* We are using a generic GET handler here */
      coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_generic);
      coap_resource_set_get_observable(r, 1);
      coap_add_resource(ctx, r);
      time_resource = r;
    }
    int
    main(int argc, char *argv[]) {
      coap_context_t *ctx = NULL;
      coap_endpoint_t *ep = NULL;
      coap_address_t addr;
      unsigned wait_ms;
      struct timeval tv_last = {0, 0};
      /* Remove (void) definition if variable is used */
      (void)argc;
      (void)argv;
      /* Initialize libcoap library */
      coap_startup();
      memset(&tv_last, 0, sizeof(tv_last));
      /* Create the libcoap context */
      ctx = coap_new_context(NULL);
      if (!ctx) {
        exit(1);
      }
      /* See coap_block(3) */
      coap_context_set_block_mode(ctx,
                                  COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
      coap_address_init(&addr);
      addr.addr.sa.sa_family = AF_INET;
      addr.addr.sin.sin_port = ntohs(COAP_DEFAULT_PORT);
      ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
      /* Other Set up Code */
      init_resources(ctx);
      if (!coap_persist_startup(ctx,
                                "/tmp/coap_dyn_resource_save_file",
                                "/tmp/coap_observe_save_file",
                                "/tmp/coap_obs_cnt_save_file", 10)) {
        fprintf(stderr, "Unable to set up persist logic\n");
        exit(1);
      }
      wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
      while (1) {
        int result = coap_io_process(ctx, wait_ms);
        if (result < 0) {
          break;
        } else if (result && (unsigned)result < wait_ms) {
          /* decrement if there is a result wait time returned */
          wait_ms -= result;
        } else {
          /*
           * result == 0, or result >= wait_ms
           * (wait_ms could have decremented to a small value, below
           * the granularity of the timer in coap_io_process() and hence
           * result == 0)
           */
          wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
        }
        if (time_resource) {
          struct timeval tv_now;
          if (gettimeofday(&tv_now, NULL) == 0) {
            if (tv_last.tv_sec != tv_now.tv_sec) {
              /* Happens once per second */
              tv_last = tv_now;
              coap_resource_notify_observers(time_resource, NULL);
            }
            /* need to wait until next second starts if wait_ms is too large */
            unsigned next_sec_ms = 1000 - (tv_now.tv_usec / 1000);
            if (next_sec_ms && next_sec_ms < wait_ms)
              wait_ms = next_sec_ms;
          }
        }
      }
      coap_persist_stop(ctx);
      coap_free_context(ctx);
      coap_cleanup();
      exit(0);
    }

SEE ALSO

coap_block(3), coap_context(3), coap_handler(3), coap_init(3), coap_observe(3), coap_pdu_setup(3), coap_resource(3) and coap_session(3)

FURTHER INFORMATION

See

"RFC7252: The Constrained Application Protocol (CoAP)"

"RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)"

"RFC8613: Object Security for Constrained RESTful Environments (OSCORE)"

for further information.

BUGS

Please raise an issue on GitHub at https://github.com/obgm/libcoap/issues to report any bugs.

Please raise a Pull Request at https://github.com/obgm/libcoap/pulls for any fixes.

AUTHORS

The libcoap project <libcoap-developers@lists.sourceforge.net>