1// -*- mode:doc; -*-
2// vim: set syntax=asciidoc tw=0
3
4coap_persist(3)
5===============
6:doctype: manpage
7:man source:   coap_persist
8:man version:  @PACKAGE_VERSION@
9:man manual:   libcoap Manual
10
11NAME
12----
13coap_persist,
14coap_persist_startup,
15coap_persist_stop,
16coap_persist_track_funcs,
17coap_persist_observe_add,
18coap_persist_set_observe_num
19- Work with CoAP persist support
20
21SYNOPSIS
22--------
23*#include <coap@LIBCOAP_API_VERSION@/coap.h>*
24
25*int coap_persist_startup(coap_context_t *_context_,
26const char *_dyn_resource_save_file_, const char *_observe_save_file_,
27const char *_obs_cnt_save_file_, uint32_t _save_freq_);*
28
29*void coap_persist_stop(coap_context_t *_context_);*
30
31*void coap_persist_track_funcs(coap_context_t *_context_,
32coap_observe_added_t _observe_added_, coap_observe_deleted_t _observe_deleted_,
33coap_track_observe_value_t _track_observe_value_,
34coap_dyn_resource_added_t _dyn_resource_added_,
35coap_resource_deleted_t _resource_deleted_,
36uint32_t _save_freq_, void *_user_data_);*
37
38*coap_subscription_t *coap_persist_observe_add(coap_context_t *_context_,
39coap_proto_t _e_proto_, const coap_address_t *_e_listen_addr_,
40const coap_addr_tuple_t *_s_addr_info_, const coap_bin_const_t *_raw_packet_,
41const coap_bin_const_t *_oscore_info_);*
42
43*void coap_persist_set_observe_num(coap_resource_t *_resource_,
44uint32_t _start_observe_no_);*
45
46For specific (D)TLS library support, link with
47*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*,
48*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*
49or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*.   Otherwise, link with
50*-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support.
51
52DESCRIPTION
53-----------
54When a coap-server is restarted, state information does not usually persist
55over the restart.  libcoap has optional compiled in support for maintaining
56resources that were dynamically created, tracking ongoing observe subscriptions
57and maintaining OSCORE protection.
58
59There are callbacks provided to support doing this as an alternative persist
60storage in the coap-server application.
61
62*NOTE:* The observe persist support is only available for UDP sessions.
63
64When using the libcoap compiled in support, only two functions need to be
65called by the application. *coap_persist_startup*() defines the file names to
66use for maintaining the persist information over an application restart, and
67*coap_persist_stop*() is called to preserve any persist information over the
68server restart.
69
70FUNCTIONS
71---------
72
73*Function: coap_persist_startup()*
74
75The *coap_persist_startup*() function is used to enable persist tracking for
76_context_ so when a coap-server is restarted, the persist tracked information
77can be added back in for the server logic.
78_dyn_resource_save_file_ is used to save the current list of resources created
79from a request to the unknown resource.
80_observe_save_file_ is used to save the current list of active observe
81subscriptions.
82_obs_cnt_save_file_ is used to save the current observe counter used when
83sending an observe unsolicited response.  _obs_cnt_save_file_ only gets
84updated every _save_freq_ updates.
85
86If any of the files exist and are not empty, when *coap_persist_startup*() is
87called, the information is loaded back into the server logic, and for the
88active observe subscriptions a new server session is created for sending out
89the ongoing observe updates (UDP only supported).
90
91If a file is defined as NULL, then that particular persist information is not
92tracked by the libcoap module.  This allows a combination of
93*coap_persist_track_funcs*() for customized persist tracking followed by a
94call to *coap_persist_startup*().
95
96*Function: coap_persist_stop()*
97
98The *coap_persist_stop*() function is used to disable any current persist
99tracking as set up by *coap_persist_startup*() for _context_ and preserve the
100tracking for when the coap-server application restarts.
101
102If using *coap_persist_track_funcs*(), then calling *coap_persist_stop*()
103will stop any 4.04 unsolicited response messages being sent when a
104resource that has an active observe subscription is deleted (as happens
105when *coap_free_context*() is subsequentially called).
106
107*Function: coap_persist_track_funcs()*
108
109The *coap_persist_track_funcs*() function is used to setup callback functions
110associated with _context_ that track information so that the current tracked
111information state can be rebuilt following a server application restart. It is
112the responsibility of the server application to track the appropriate
113information.
114
115The _observe_added_ callback function prototype, called when a client
116subscribes to a resource for observation, is defined as:
117[source, c]
118----
119typedef int (*coap_observe_added_t)(coap_session_t *session,
120                                    coap_subscription_t *observe_key,
121                                    coap_proto_t e_proto,
122                                    coap_address_t *e_listen_addr,
123                                    coap_addr_tuple_t *s_addr_info,
124                                    coap_bin_const_t *raw_packet,
125                                    coap_bin_const_t *oscore_info,
126                                    void *user_data);
127----
128
129The _observe_deleted_ callback function prototype, called when a client
130removes the subscription to a resource for observation, is defined as:
131[source, c]
132----
133typedef int (*coap_observe_deleted_t)(coap_session_t *session,
134                                      coap_subscription_t *observe_key,
135                                      void *user_data);
136----
137
138The _track_observe_value_ callback function prototype, called when a new
139unsolicited observe response is went (every _save_freq_), is defined as:
140[source, c]
141----
142typedef int (*coap_track_observe_value_t)(coap_context_t *context,
143                                          coap_str_const_t *resource_name,
144                                          uint32_t observe_num,
145                                          void *user_data);
146----
147
148The _dyn_resource_added_ callback function prototype, called whenever a
149resource is created from a request that is calling the resource unknown
150handler, is defined as:
151[source, c]
152----
153typedef int (*coap_dyn_resource_added_t)(coap_session_t *session,
154                                         coap_str_const_t *resource_name,
155                                         coap_bin_const_t *raw_packet,
156                                         void *user_data);
157----
158
159The _resource_deleted_ callback function prototype, called whenever a
160resource is deleted, is defined as:
161[source, c]
162----
163typedef int (*coap_resource_deleted_t)(coap_context_t *context,
164                                       coap_str_const_t *resource_name,
165                                       void *user_data);
166----
167
168_save_freq_ defines the frequency of the update to the observe value when
169libcoap calls _track_observe_value_. _user_data_ is application defined and
170is passed into all of the callback handlers.
171
172*Function: coap_persist_observe_add()*
173
174The *coap_persist_observe_add*() function is used to set up a session and a
175observe subscription request (typically following a server reboot) so that a
176client can continue to receive unsolicited observe responses without having
177to establish a new session and issue a new observe subscription request. The
178new session is associated with the endpoint defined by _e_proto_ and
179_e_listen_address_.  The session has the IP addresses as defined by
180_s_addr_info_. _raw_packet_ contains the layer 7 of the IP packet that was
181originally used to request the observe subscription. Optional _oscore_info_
182defines the OSCORE information if packets are protected by OSCORE.
183 _e_proto_, _e_listen_addr_, _s_addr_info_, _raw_packet_ and _oscore_info_
184are the same as passed into the _coap_observe_added_t_ callback.
185
186*Function: coap_persist_set_observe_num()*
187
188The *coap_persist_set_observe_num*() function is used to update the
189_resource_'s current observe counter to start from _start_observe_no_
190instead of 0,
191
192RETURN VALUES
193-------------
194*coap_persist_startup*() returns 1 on success else 0.
195
196*coap_persist_observe_add*() returns a newly created observe
197subscription or NULL on failure.
198
199EXAMPLES
200--------
201*Simple Time Server*
202
203[source, c]
204----
205#include <coap@LIBCOAP_API_VERSION@/coap.h>
206
207#include <stdio.h>
208
209coap_resource_t *time_resource = NULL;
210
211/* specific GET "time" handler, called from hnd_get_generic() */
212
213static void
214hnd_get_time(coap_resource_t *resource, coap_session_t *session,
215const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) {
216
217  unsigned char buf[40];
218  size_t len;
219  time_t now;
220  (void)resource;
221  (void)session;
222
223  /* ... Additional analysis code for resource, request pdu etc.  ... */
224
225  /* After analysis, generate a suitable response */
226
227  /* Note that token, if set, is already in the response pdu */
228
229  now = time(NULL);
230
231  if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
232    /* Output secs since Jan 1 1970 */
233    len = snprintf((char *)buf, sizeof(buf), "%lu", now);
234  }
235  else {
236    /* Output human-readable time */
237    struct tm *tmp;
238    tmp = gmtime(&now);
239    if (!tmp) {
240      /* If 'now' is not valid */
241      coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
242      return;
243    }
244    len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
245  }
246  coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
247  /*
248   * Invoke coap_add_data_large_response() to do all the hard work.
249   *
250   * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in
251   * Define how long this response is valid for (secs) - 1 - to add in.
252   * ETAG Option added internally with unique value as param set to 0
253   *
254   * OBSERVE Option added internally if needed within the function
255   * BLOCK2 Option added internally if output too large
256   * SIZE2 Option added internally
257   */
258  coap_add_data_large_response(resource, session, request, response,
259                               query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
260                               len,
261                               buf, NULL, NULL);
262}
263
264/* Generic GET handler */
265
266static void
267hnd_get_generic(coap_resource_t *resource, coap_session_t *session,
268const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) {
269
270  coap_str_const_t *uri_path = coap_resource_get_uri_path(resource);
271
272  if (!uri_path) {
273    /* Unexpected Failure */
274    coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
275    return;
276  }
277
278  /* Is this the "time" resource" ? */
279  if (coap_string_equal(uri_path, coap_make_str_const("time"))) {
280    hnd_get_time(resource, session, request, query, response);
281    return;
282  }
283
284  /* Other resources code */
285
286  /* Failure response */
287  coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
288}
289
290/* Initialize generic GET handler */
291
292static void
293init_resources(coap_context_t *ctx)
294{
295
296  coap_resource_t *r;
297
298  /* Create a resource to return return or update time */
299  r = coap_resource_init(coap_make_str_const("time"),
300                         COAP_RESOURCE_FLAGS_NOTIFY_CON);
301
302  /* We are using a generic GET handler here */
303  coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_generic);
304
305  coap_resource_set_get_observable(r, 1);
306
307  coap_add_resource(ctx, r);
308  time_resource = r;
309
310}
311
312int
313main(int argc, char *argv[]) {
314
315  coap_context_t *ctx = NULL;
316  coap_endpoint_t *ep = NULL;
317  coap_address_t addr;
318  unsigned wait_ms;
319  struct timeval tv_last = {0, 0};
320  /* Remove (void) definition if variable is used */
321  (void)argc;
322  (void)argv;
323
324  /* Initialize libcoap library */
325  coap_startup();
326
327  memset (&tv_last, 0, sizeof(tv_last));
328
329  /* Create the libcoap context */
330  ctx = coap_new_context(NULL);
331  if (!ctx) {
332    exit(1);
333  }
334  /* See coap_block(3) */
335  coap_context_set_block_mode(ctx,
336                              COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
337
338  coap_address_init(&addr);
339  addr.addr.sa.sa_family = AF_INET;
340  addr.addr.sin.sin_port = ntohs(COAP_DEFAULT_PORT);
341  ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
342
343  /* Other Set up Code */
344
345  init_resources(ctx);
346
347  if (!coap_persist_startup(ctx,
348                            "/tmp/coap_dyn_resource_save_file",
349                            "/tmp/coap_observe_save_file",
350                            "/tmp/coap_obs_cnt_save_file", 10)) {
351    fprintf(stderr, "Unable to set up persist logic\n");
352    exit(1);
353  }
354
355  wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
356
357  while (1) {
358    int result = coap_io_process( ctx, wait_ms );
359    if ( result < 0 ) {
360      break;
361    } else if ( result && (unsigned)result < wait_ms ) {
362      /* decrement if there is a result wait time returned */
363      wait_ms -= result;
364    } else {
365      /*
366       * result == 0, or result >= wait_ms
367       * (wait_ms could have decremented to a small value, below
368       * the granularity of the timer in coap_io_process() and hence
369       * result == 0)
370       */
371      wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
372    }
373    if (time_resource) {
374      struct timeval tv_now;
375      if (gettimeofday (&tv_now, NULL) == 0) {
376        if (tv_last.tv_sec != tv_now.tv_sec) {
377          /* Happens once per second */
378          tv_last = tv_now;
379          coap_resource_notify_observers(time_resource, NULL);
380        }
381        /* need to wait until next second starts if wait_ms is too large */
382        unsigned next_sec_ms = 1000 - (tv_now.tv_usec / 1000);
383
384        if (next_sec_ms && next_sec_ms < wait_ms)
385          wait_ms = next_sec_ms;
386      }
387    }
388  }
389  coap_persist_stop(ctx);
390  coap_free_context(ctx);
391  coap_cleanup();
392  exit(0);
393
394}
395----
396
397SEE ALSO
398--------
399*coap_block*(3), *coap_context*(3), *coap_handler*(3), *coap_init*(3), *coap_observe*(3),
400*coap_pdu_setup*(3), *coap_resource*(3) and *coap_session*(3)
401
402FURTHER INFORMATION
403-------------------
404See
405
406"https://rfc-editor.org/rfc/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]"
407
408"https://rfc-editor.org/rfc/rfc7641[RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)]"
409
410"https://rfc-editor.org/rfc/rfc8613[RFC8613: Object Security for Constrained RESTful Environments (OSCORE)]"
411
412for further information.
413
414
415BUGS
416----
417Please report bugs on the mailing list for libcoap:
418libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
419https://github.com/obgm/libcoap/issues
420
421AUTHORS
422-------
423The libcoap project <libcoap-developers@lists.sourceforge.net>
424