Imported Upstream version 0.9.1
[platform/upstream/iotivity.git] / resource / csdk / connectivity / lib / libcoap-4.1.1 / examples / etsi_iot_01.c
1 /* CoAP server for first ETSI CoAP plugtest, March 2012
2  *
3  * Copyright (C) 2012--2013 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use. 
7  */
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <sys/select.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <netdb.h>
20 #include <sys/stat.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <signal.h>
24
25 #include "config.h"
26 #include "uthash.h"
27 #include "coap.h"
28
29 #define COAP_RESOURCE_CHECK_TIME_SEC  1
30
31 #ifndef min
32 #define min(a,b) ((a) < (b) ? (a) : (b))
33 #endif
34
35 /* temporary storage for dynamic resource representations */
36 static int quit = 0;
37
38 #define COAP_OPT_BLOCK_SZX_MAX 6 /**< allowed maximum for block szx value */
39
40 #define REQUIRE_ETAG 0x01   /* flag for coap_payload_t: require ETag option  */
41 typedef struct
42 {
43     UT_hash_handle hh;
44     coap_key_t resource_key; /* foreign key that points into resource space */
45     unsigned int flags; /* some flags to control behavior */
46     size_t max_data; /* maximum size allocated for @p data */
47     uint16_t media_type; /* media type for this object */
48     size_t length; /* length of data */
49     unsigned char data[]; /* the actual contents */
50 } coap_payload_t;
51
52 coap_payload_t *test_resources = NULL;
53
54 /** 
55  * This structure is used to store URIs for dynamically allocated
56  * resources, usually by POST or PUT.
57  */
58 typedef struct
59 {
60     UT_hash_handle hh;
61     coap_key_t resource_key; /* foreign key that points into resource space */
62     size_t length; /* length of data */
63     unsigned char data[]; /* the actual contents */
64 } coap_dynamic_uri_t;
65
66 coap_dynamic_uri_t *test_dynamic_uris = NULL;
67
68 /* This variable is used to mimic long-running tasks that require
69  * asynchronous responses. */
70 static coap_async_state_t *async = NULL;
71
72 /* SIGINT handler: set quit to 1 for graceful termination */
73 void handle_sigint(int signum)
74 {
75     quit = 1;
76 }
77
78 #define INDEX "libcoap server for ETSI CoAP Plugtest, March 2012, Paris\n" \
79           "Copyright (C) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
80
81 coap_payload_t *
82 coap_new_payload(size_t size)
83 {
84     coap_payload_t *p;
85     p = (coap_payload_t *) coap_malloc(sizeof(coap_payload_t) + size);
86     if (p)
87     {
88         memset(p, 0, sizeof(coap_payload_t));
89         p->max_data = size;
90     }
91
92     return p;
93 }
94
95 static inline coap_payload_t *
96 coap_find_payload(const coap_key_t key)
97 {
98     coap_payload_t *p;
99     HASH_FIND(hh, test_resources, key, sizeof(coap_key_t), p);
100     return p;
101 }
102
103 static inline void coap_add_payload(const coap_key_t key, coap_payload_t *payload,
104         coap_dynamic_uri_t *uri)
105 {
106     assert(payload);
107
108     memcpy(payload->resource_key, key, sizeof(coap_key_t));
109     HASH_ADD(hh, test_resources, resource_key, sizeof(coap_key_t), payload);
110
111     if (uri)
112     {
113         memcpy(uri->resource_key, key, sizeof(coap_key_t));
114         HASH_ADD(hh, test_dynamic_uris, resource_key, sizeof(coap_key_t), uri);
115     }
116 }
117
118 static inline void coap_delete_payload(coap_payload_t *payload)
119 {
120     if (payload)
121     {
122         coap_dynamic_uri_t *uri;
123         HASH_FIND(hh, test_dynamic_uris, payload->resource_key, sizeof(coap_key_t), uri);
124         if (uri)
125         {
126             HASH_DELETE(hh, test_dynamic_uris, uri);
127             coap_free(uri);
128         }
129     }
130
131     HASH_DELETE(hh, test_resources, payload);
132     coap_free(payload);
133 }
134
135 void hnd_get_index(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
136         coap_pdu_t *request, str *token, coap_pdu_t *response)
137 {
138     unsigned char buf[3];
139
140     response->hdr->code = COAP_RESPONSE_CODE(205);
141
142     coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
143             coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
144
145     coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_bytes(buf, 0x2ffff), buf);
146
147     coap_add_data(response, strlen(INDEX), (unsigned char *) INDEX);
148 }
149
150 void hnd_get_resource(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
151         coap_pdu_t *request, str *token, coap_pdu_t *response)
152 {
153     coap_key_t etag;
154     unsigned char buf[2];
155     coap_payload_t *test_payload;
156     coap_block_t block;
157
158     test_payload = coap_find_payload(resource->key);
159     if (!test_payload)
160     {
161         response->hdr->code = COAP_RESPONSE_CODE(500);
162
163         return;
164     }
165
166     response->hdr->code = COAP_RESPONSE_CODE(205);
167
168     coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
169             coap_encode_var_bytes(buf, test_payload->media_type), buf);
170
171     /* add etag for the resource */
172     if (test_payload->flags & REQUIRE_ETAG)
173     {
174         memset(etag, 0, sizeof(etag));
175         coap_hash(test_payload->data, test_payload->length, etag);
176         coap_add_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
177     }
178
179     if (request)
180     {
181         int res;
182
183         if (coap_get_block(request, COAP_OPTION_BLOCK2, &block))
184         {
185             res = coap_write_block_opt(&block, COAP_OPTION_BLOCK2, response, test_payload->length);
186
187             switch (res)
188             {
189                 case -2: /* illegal block */
190                     response->hdr->code = COAP_RESPONSE_CODE(400);
191                     goto error;
192                 case -1: /* should really not happen */
193                     assert(0);
194                     /* fall through if assert is a no-op */
195                 case -3: /* cannot handle request */
196                     response->hdr->code = COAP_RESPONSE_CODE(500);
197                     goto error;
198                 default: /* everything is good */
199                     ;
200             }
201
202             coap_add_block(response, test_payload->length, test_payload->data, block.num,
203                     block.szx);
204         }
205         else
206         {
207             if (!coap_add_data(response, test_payload->length, test_payload->data))
208             {
209                 /* set initial block size, will be lowered by
210                  * coap_write_block_opt) automatically */
211                 block.szx = 6;
212                 coap_write_block_opt(&block, COAP_OPTION_BLOCK2, response, test_payload->length);
213
214                 coap_add_block(response, test_payload->length, test_payload->data, block.num,
215                         block.szx);
216             }
217         }
218     }
219     else
220     { /* this is a notification, block is 0 */
221         /* FIXME: need to store block size with subscription */
222     }
223
224     return;
225
226     error: coap_add_data(response, strlen(coap_response_phrase(response->hdr->code)),
227             (unsigned char *) coap_response_phrase(response->hdr->code));
228 }
229
230 /* DELETE handler for dynamic resources created by POST /test */
231 void hnd_delete_resource(coap_context_t *ctx, struct coap_resource_t *resource,
232         coap_address_t *peer, coap_pdu_t *request, str *token, coap_pdu_t *response)
233 {
234     coap_payload_t *payload;
235
236     payload = coap_find_payload(resource->key);
237
238     if (payload)
239         coap_delete_payload(payload);
240
241     coap_delete_resource(ctx, resource->key);
242
243     response->hdr->code = COAP_RESPONSE_CODE(202);
244 }
245
246 void hnd_post_test(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
247         coap_pdu_t *request, str *token, coap_pdu_t *response)
248 {
249     coap_opt_iterator_t opt_iter;
250     coap_opt_t *option;
251     coap_payload_t *test_payload;
252     size_t len;
253     size_t l = 6 + sizeof(void *);
254     coap_dynamic_uri_t *uri;
255     unsigned char *data;
256
257 #define BUFSIZE 20
258     int res;
259     unsigned char _buf[BUFSIZE];
260     unsigned char *buf = _buf;
261     size_t buflen = BUFSIZE;
262
263     coap_get_data(request, &len, &data);
264
265     /* allocate storage for resource and to hold URI */
266     test_payload = coap_new_payload(len);
267     uri = (coap_dynamic_uri_t *) coap_malloc(sizeof(coap_dynamic_uri_t) + l);
268     if (!(test_payload && uri))
269     {
270         coap_log(LOG_CRIT, "cannot allocate new resource under /test");
271         response->hdr->code = COAP_RESPONSE_CODE(500);
272         coap_free(test_payload);
273         coap_free(uri);
274     }
275     else
276     {
277         coap_resource_t *r;
278
279         memset(uri, 0, sizeof(coap_dynamic_uri_t));
280         uri->length = min(l, snprintf((char *)uri->data, l, "test/%p", test_payload));
281         test_payload->length = len;
282
283         memcpy(test_payload->data, data, len);
284
285         r = coap_resource_init(uri->data, uri->length, 0);
286         coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
287         coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
288
289         /* set media_type if available */
290         option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
291         if (option)
292         {
293             test_payload->media_type = coap_decode_var_bytes(COAP_OPT_VALUE(option),
294                     COAP_OPT_LENGTH(option));
295         }
296
297         coap_add_resource(ctx, r);
298         coap_add_payload(r->key, test_payload, uri);
299
300         /* add Location-Path */
301         res = coap_split_path(uri->data, uri->length, buf, &buflen);
302
303         while (res--)
304         {
305             coap_add_option(response, COAP_OPTION_LOCATION_PATH, COAP_OPT_LENGTH(buf),
306                     COAP_OPT_VALUE(buf));
307
308             buf += COAP_OPT_SIZE(buf);
309         }
310
311         response->hdr->code = COAP_RESPONSE_CODE(201);
312     }
313
314 }
315
316 void hnd_put_test(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
317         coap_pdu_t *request, str *token, coap_pdu_t *response)
318 {
319     coap_opt_iterator_t opt_iter;
320     coap_opt_t *option;
321     coap_payload_t *payload;
322     size_t len;
323     unsigned char *data;
324
325     response->hdr->code = COAP_RESPONSE_CODE(204);
326
327     coap_get_data(request, &len, &data);
328
329     payload = coap_find_payload(resource->key);
330     if (payload && payload->max_data < len)
331     { /* need more storage */
332         coap_delete_payload(payload);
333         payload = NULL;
334         /* bug: when subsequent coap_new_payload() fails, our old contents
335          is gone */
336     }
337
338     if (!payload)
339     { /* create new payload */
340         payload = coap_new_payload(len);
341         if (!payload)
342             goto error;
343
344         coap_add_payload(resource->key, payload, NULL);
345     }
346     payload->length = len;
347     memcpy(payload->data, data, len);
348
349     option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
350     if (option)
351     {
352         /* set media type given in request */
353         payload->media_type = coap_decode_var_bytes(COAP_OPT_VALUE(option),
354                 COAP_OPT_LENGTH(option));
355     }
356     else
357     {
358         /* set default value */
359         payload->media_type = COAP_MEDIATYPE_TEXT_PLAIN;
360     }
361     /* FIXME: need to change attribute ct of resource. 
362      To do so, we need dynamic management of the attribute value
363      */
364
365     return;
366     error:
367     warn("cannot modify resource\n");
368     response->hdr->code = COAP_RESPONSE_CODE(500);
369 }
370
371 void hnd_delete_test(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
372         coap_pdu_t *request, str *token, coap_pdu_t *response)
373 {
374     /* the ETSI validation tool does not like empty resources... */
375 #if 0
376     coap_payload_t *payload;
377     payload = coap_find_payload(resource->key);
378
379     if (payload)
380     payload->length = 0;
381 #endif
382
383     response->hdr->code = COAP_RESPONSE_CODE(202);
384 }
385
386 void hnd_get_query(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
387         coap_pdu_t *request, str *token, coap_pdu_t *response)
388 {
389     coap_opt_iterator_t opt_iter;
390     coap_opt_filter_t f;
391     coap_opt_t *q;
392     size_t len, L;
393     unsigned char buf[70];
394
395     response->hdr->code = COAP_RESPONSE_CODE(205);
396
397     coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
398             coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
399
400     coap_option_filter_clear(f);
401     coap_option_setb(f, COAP_OPTION_URI_QUERY);
402
403     coap_option_iterator_init(request, &opt_iter, f);
404
405     len = 0;
406     while ((len < sizeof(buf)) && (q = coap_option_next(&opt_iter)))
407     {
408         L = min(sizeof(buf) - len, 11);
409         memcpy(buf + len, "Uri-Query: ", L);
410         len += L;
411
412         L = min(sizeof(buf) - len, COAP_OPT_LENGTH(q));
413         memcpy(buf + len, COAP_OPT_VALUE(q), L);
414         len += L;
415
416         if (len < sizeof(buf))
417             buf[len++] = '\n';
418     }
419
420     coap_add_data(response, len, buf);
421 }
422
423 /* handler for TD_COAP_CORE_16 */
424 void hnd_get_separate(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
425         coap_pdu_t *request, str *token, coap_pdu_t *response)
426 {
427     coap_opt_iterator_t opt_iter;
428     coap_opt_t *option;
429     coap_opt_filter_t f;
430     unsigned long delay = 5;
431
432     if (async)
433     {
434         if (async->id != request->hdr->id)
435         {
436             coap_opt_filter_t f;
437             coap_option_filter_clear(f);
438             response->hdr->code = COAP_RESPONSE_CODE(503);
439         }
440         return;
441     }
442
443     /* search for option delay in query list */
444     coap_option_filter_clear(f);
445     coap_option_setb(f, COAP_OPTION_URI_QUERY);
446
447     coap_option_iterator_init(request, &opt_iter, f);
448
449     while ((option = coap_option_next(&opt_iter)))
450     {
451         if (strncmp("delay=", (char *) COAP_OPT_VALUE(option), 6) == 0)
452         {
453             int i;
454             unsigned long d = 0;
455
456             for (i = 6; i < COAP_OPT_LENGTH(option); ++i)
457                 d = d * 10 + COAP_OPT_VALUE(option)[i] - '0';
458
459             /* don't allow delay to be less than COAP_RESOURCE_CHECK_TIME*/
460             delay = d < COAP_RESOURCE_CHECK_TIME_SEC ? COAP_RESOURCE_CHECK_TIME_SEC : d;
461             debug("set delay to %lu\n", delay);
462             break;
463         }
464     }
465
466     async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE,
467             (void *) (COAP_TICKS_PER_SECOND * delay));
468 }
469
470 void check_async(coap_context_t *ctx, coap_tick_t now)
471 {
472     coap_pdu_t *response;
473     coap_async_state_t *tmp;
474     unsigned char buf[2];
475     size_t size = sizeof(coap_hdr_t) + 8;
476
477     if (!async || now < async->created + (unsigned long) async->appdata)
478         return;
479
480     size += async->tokenlen;
481
482     response = coap_pdu_init(async->flags & COAP_ASYNC_CONFIRM ? COAP_MESSAGE_CON : COAP_MESSAGE_NON,
483     COAP_RESPONSE_CODE(205), 0, size);
484     if (!response)
485     {
486         debug("check_async: insufficient memory, we'll try later\n");
487         async->appdata = (void *) ((unsigned long) async->appdata + 15 * COAP_TICKS_PER_SECOND);
488         return;
489     }
490
491     response->hdr->id = coap_new_message_id(ctx);
492
493     if (async->tokenlen)
494         coap_add_token(response, async->tokenlen, async->token);
495
496     coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
497             coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
498
499     coap_add_data(response, 4, (unsigned char *) "done");
500
501     if (coap_send(ctx, &async->peer, response) == COAP_INVALID_TID)
502     {
503         debug("check_async: cannot send response for message %d\n", response->hdr->id);
504     }
505     coap_delete_pdu(response);
506
507     coap_remove_async(ctx, async->id, &tmp);
508     coap_free_async(async);
509     async = NULL;
510 }
511
512 coap_payload_t *
513 make_large(char *filename)
514 {
515     coap_payload_t *payload;
516     FILE *inputfile = NULL;
517     struct stat statbuf;
518
519     if (!filename)
520         return NULL;
521
522     /* read from specified input file */
523     if (stat(filename, &statbuf) < 0)
524     {
525         warn("cannot stat file %s\n", filename);
526         return NULL;
527     }
528
529     payload = coap_new_payload(statbuf.st_size);
530     if (!payload)
531         return NULL;
532
533     inputfile = fopen(filename, "r");
534     if (!inputfile)
535     {
536         warn("cannot read file %s\n", filename);
537         coap_free(payload);
538         return NULL;
539     }
540
541     payload->length = fread(payload->data, 1, statbuf.st_size, inputfile);
542     payload->media_type = 41;
543
544     fclose(inputfile);
545
546     return payload;
547 }
548
549 void init_resources(coap_context_t *ctx)
550 {
551     coap_resource_t *r;
552     coap_payload_t *test_payload;
553
554     test_payload = coap_new_payload(200);
555     if (!test_payload)
556         coap_log(LOG_CRIT, "cannot allocate resource /test");
557     else
558     {
559         test_payload->length = 13;
560         memcpy(test_payload->data, "put data here", test_payload->length);
561         /* test_payload->media_type is 0 anyway */
562
563         r = coap_resource_init((unsigned char *) "test", 4, 0);
564         coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
565         coap_register_handler(r, COAP_REQUEST_POST, hnd_post_test);
566         coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_test);
567         coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_test);
568
569         coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
570         coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "test", 4, 0);
571         coap_add_attr(r, (unsigned char *) "if", 2, (unsigned char *) "core#b", 6, 0);
572 #if 0
573         coap_add_attr(r, (unsigned char *)"obs", 3, NULL, 0, 0);
574 #endif
575         coap_add_resource(ctx, r);
576         coap_add_payload(r->key, test_payload, NULL);
577     }
578
579     /* TD_COAP_BLOCK_01 
580      * TD_COAP_BLOCK_02 */
581     test_payload = make_large("etsi_iot_01_largedata.txt");
582     if (!test_payload)
583         coap_log(LOG_CRIT, "cannot allocate resource /large\n");
584     else
585     {
586         r = coap_resource_init((unsigned char *) "large", 5, 0);
587         coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
588
589         coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "41", 2, 0);
590         coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "large", 5, 0);
591         coap_add_resource(ctx, r);
592
593         test_payload->flags |= REQUIRE_ETAG;
594
595         coap_add_payload(r->key, test_payload, NULL);
596     }
597
598     /* For TD_COAP_CORE_12 */
599     test_payload = coap_new_payload(20);
600     if (!test_payload)
601         coap_log(LOG_CRIT, "cannot allocate resource /seg1/seg2/seg3\n");
602     else
603     {
604         test_payload->length = 10;
605         memcpy(test_payload->data, "segsegseg!", test_payload->length);
606         /* test_payload->media_type is 0 anyway */
607
608         r = coap_resource_init((unsigned char *) "seg1/seg2/seg3", 14, 0);
609         coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
610
611         coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
612         coap_add_resource(ctx, r);
613
614         coap_add_payload(r->key, test_payload, NULL);
615     }
616
617     /* For TD_COAP_CORE_13 */
618     r = coap_resource_init((unsigned char *) "query", 5, 0);
619     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_query);
620
621     coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
622     coap_add_resource(ctx, r);
623
624     /* For TD_COAP_CORE_16 */
625     r = coap_resource_init((unsigned char *) "separate", 8, 0);
626     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_separate);
627
628     coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
629     coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "separate", 8, 0);
630     coap_add_resource(ctx, r);
631 }
632
633 void usage(const char *program, const char *version)
634 {
635     const char *p;
636
637     p = strrchr(program, '/');
638     if (p)
639         program = ++p;
640
641     fprintf(stderr, "%s v%s -- ETSI CoAP plugtest server\n"
642             "(c) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
643             "usage: %s [-A address] [-p port]\n\n"
644             "\t-A address\tinterface address to bind to\n"
645             "\t-p port\t\tlisten on specified port\n"
646             "\t-v num\t\tverbosity level (default: 3)\n", program, version, program);
647 }
648
649 coap_context_t *
650 get_context(const char *node, const char *port)
651 {
652     coap_context_t *ctx = NULL;
653     int s;
654     struct addrinfo hints;
655     struct addrinfo *result, *rp;
656
657     memset(&hints, 0, sizeof(struct addrinfo));
658     hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
659     hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
660     hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
661
662     s = getaddrinfo(node, port, &hints, &result);
663     if (s != 0)
664     {
665         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
666         return NULL;
667     }
668
669     /* iterate through results until success */
670     for (rp = result; rp != NULL; rp = rp->ai_next)
671     {
672         coap_address_t addr;
673
674         if (rp->ai_addrlen <= sizeof(addr.addr))
675         {
676             coap_address_init(&addr);
677             addr.size = rp->ai_addrlen;
678             memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
679
680             ctx = coap_new_context(&addr);
681             if (ctx)
682             {
683                 /* TODO: output address:port for successful binding */
684                 goto finish;
685             }
686         }
687     }
688
689     fprintf(stderr, "no context available for interface '%s'\n", node);
690
691     finish: freeaddrinfo(result);
692     return ctx;
693 }
694
695 int main(int argc, char **argv)
696 {
697     coap_context_t *ctx;
698     fd_set readfds;
699     struct timeval tv, *timeout;
700     int result;
701     coap_tick_t now;
702     coap_queue_t *nextpdu;
703     char addr_str[NI_MAXHOST] = "::";
704     char port_str[NI_MAXSERV] = "5683";
705     int opt;
706     coap_log_t log_level = LOG_WARNING;
707
708     while ((opt = getopt(argc, argv, "A:p:v:")) != -1)
709     {
710         switch (opt)
711         {
712             case 'A':
713                 strncpy(addr_str, optarg, NI_MAXHOST - 1);
714                 addr_str[NI_MAXHOST - 1] = '\0';
715                 break;
716             case 'p':
717                 strncpy(port_str, optarg, NI_MAXSERV - 1);
718                 port_str[NI_MAXSERV - 1] = '\0';
719                 break;
720             case 'v':
721                 log_level = strtol(optarg, NULL, 10);
722                 break;
723             default:
724                 usage(argv[0], PACKAGE_VERSION);
725                 exit(1);
726         }
727     }
728
729     coap_set_log_level(log_level);
730
731     ctx = get_context(addr_str, port_str);
732     if (!ctx)
733         return -1;
734
735     coap_register_option(ctx, COAP_OPTION_BLOCK2);
736
737     init_resources(ctx);
738
739     signal(SIGINT, handle_sigint);
740
741     while (!quit)
742     {
743         FD_ZERO(&readfds);
744         FD_SET(ctx->sockfd, &readfds);
745
746         nextpdu = coap_peek_next(ctx);
747
748         coap_ticks(&now);
749         while (nextpdu && nextpdu->t <= now)
750         {
751             coap_retransmit(ctx, coap_pop_next(ctx));
752             nextpdu = coap_peek_next(ctx);
753         }
754
755         if (nextpdu && nextpdu->t <= now + COAP_RESOURCE_CHECK_TIME_SEC)
756         {
757             /* set timeout if there is a pdu to send before our automatic timeout occurs */
758             tv.tv_usec = ((nextpdu->t - now) % COAP_TICKS_PER_SECOND) * 1000000
759                     / COAP_TICKS_PER_SECOND;
760             tv.tv_sec = (nextpdu->t - now) / COAP_TICKS_PER_SECOND;
761             timeout = &tv;
762         }
763         else
764         {
765             tv.tv_usec = 0;
766             tv.tv_sec = COAP_RESOURCE_CHECK_TIME_SEC;
767             timeout = &tv;
768         }
769         result = select(FD_SETSIZE, &readfds, 0, 0, timeout);
770
771         if (result < 0)
772         { /* error */
773             if (errno != EINTR)
774                 perror("select");
775         }
776         else if (result > 0)
777         { /* read from socket */
778             if (FD_ISSET(ctx->sockfd, &readfds))
779             {
780                 coap_read(ctx); /* read received data */
781                 coap_dispatch(ctx); /* and dispatch PDUs from receivequeue */
782             }
783         }
784         else
785         { /* timeout */
786             /* coap_check_resource_list( ctx ); */
787         }
788
789         /* check if we have to send asynchronous responses */
790         check_async(ctx, now);
791     }
792
793     coap_free_context(ctx);
794
795     return 0;
796 }