blob: b11a1c0568538d68b175ad493079b496601491c2 [file] [log] [blame]
/*******************************************************************************
*
* Copyright (c) 2013, 2014, 2015 Intel Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* The Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* David Navarro, Intel Corporation - initial API and implementation
* Benjamin Cabé - Please refer to git log
* Fabien Fleutot - Please refer to git log
* Simon Bernard - Please refer to git log
* Julien Vermillard - Please refer to git log
* Axel Lorente - Please refer to git log
* Toby Jaffey - Please refer to git log
* Bosch Software Innovations GmbH - Please refer to git log
* Pascal Rieux - Please refer to git log
*
*******************************************************************************/
/*
Copyright (c) 2013, 2014 Intel Corporation
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
David Navarro <david.navarro@intel.com>
Bosch Software Innovations GmbH - Please refer to git log
*/
#include "liblwm2m.h"
#include "connection.h"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
extern lwm2m_object_t * get_object_device(void);
extern lwm2m_object_t * get_server_object(void);
extern lwm2m_object_t * get_security_object(void);
extern char * get_server_uri(lwm2m_object_t * objectP, uint16_t secObjInstID);
extern lwm2m_object_t * get_test_object(void);
#define MAX_PACKET_SIZE 1024
int g_reboot = 0;
static int g_quit = 0;
typedef struct
{
lwm2m_object_t * securityObjP;
int sock;
connection_t * connList;
} client_data_t;
void handle_sigint(int signum)
{
g_quit = 1;
}
static void * prv_connect_server(uint16_t secObjInstID,
void * userData)
{
client_data_t * dataP;
char * uri;
char * host;
char * port;
connection_t * newConnP = NULL;
dataP = (client_data_t *)userData;
uri = get_server_uri(dataP->securityObjP, secObjInstID);
if (uri == NULL) return NULL;
fprintf(stdout, "Connecting to %s\r\n", uri);
// parse uri in the form "coaps://[host]:[port]"
if (0 == strncmp(uri, "coaps://", strlen("coaps://")))
{
host = uri+strlen("coaps://");
}
else if (0 == strncmp(uri, "coap://", strlen("coap://")))
{
host = uri+strlen("coap://");
}
else
{
goto exit;
}
port = strrchr(host, ':');
if (port == NULL) goto exit;
// remove brackets
if (host[0] == '[')
{
host++;
if (*(port - 1) == ']')
{
*(port - 1) = 0;
}
else goto exit;
}
// split strings
*port = 0;
port++;
newConnP = connection_create(dataP->connList, dataP->sock, host, port);
if (newConnP == NULL) {
fprintf(stderr, "Connection creation failed.\r\n");
}
else {
dataP->connList = newConnP;
}
exit:
lwm2m_free(uri);
return (void *)newConnP;
}
static uint8_t prv_buffer_send(void * sessionH,
uint8_t * buffer,
size_t length,
void * userdata)
{
connection_t * connP = (connection_t*) sessionH;
if (connP == NULL)
{
fprintf(stderr, "#> failed sending %lu bytes, missing connection\r\n", length);
return COAP_500_INTERNAL_SERVER_ERROR ;
}
if (-1 == connection_send(connP, buffer, length))
{
fprintf(stderr, "#> failed sending %lu bytes\r\n", length);
return COAP_500_INTERNAL_SERVER_ERROR ;
}
return COAP_NO_ERROR;
}
void print_usage(void)
{
fprintf(stdout, "Usage: lwm2mclient [OPTION]\r\n");
fprintf(stdout, "Launch a LWM2M client.\r\n");
fprintf(stdout, "Options:\r\n");
fprintf(stdout, " -n NAME\tSet the endpoint name of the Client. Default: testlightclient\r\n");
fprintf(stdout, " -l PORT\tSet the local UDP port of the Client. Default: 56830\r\n");
fprintf(stdout, "\r\n");
}
#define OBJ_COUNT 4
int main(int argc, char *argv[])
{
client_data_t data;
lwm2m_context_t * lwm2mH = NULL;
lwm2m_object_t * objArray[OBJ_COUNT];
const char * localPort = "56830";
char * name = "testlwm2mclient";
int result;
int i;
int opt;
memset(&data, 0, sizeof(client_data_t));
while ((opt = getopt(argc, argv, "l:n:t:")) != -1)
{
switch (opt)
{
case 'n':
name = optarg;
break;
case 'l':
localPort = optarg;
break;
default:
print_usage();
return 0;
}
}
/*
*This call an internal function that create an IPv6 socket on the port 5683.
*/
fprintf(stderr, "Trying to bind LWM2M Client to port %s\r\n", localPort);
data.sock = create_socket(localPort);
if (data.sock < 0)
{
fprintf(stderr, "Failed to open socket: %d\r\n", errno);
return -1;
}
/*
* Now the main function fill an array with each object, this list will be later passed to liblwm2m.
* Those functions are located in their respective object file.
*/
objArray[0] = get_security_object();
if (NULL == objArray[0])
{
fprintf(stderr, "Failed to create security object\r\n");
return -1;
}
data.securityObjP = objArray[0];
objArray[1] = get_server_object();
if (NULL == objArray[1])
{
fprintf(stderr, "Failed to create server object\r\n");
return -1;
}
objArray[2] = get_object_device();
if (NULL == objArray[2])
{
fprintf(stderr, "Failed to create Device object\r\n");
return -1;
}
objArray[3] = get_test_object();
if (NULL == objArray[3])
{
fprintf(stderr, "Failed to create Test object\r\n");
return -1;
}
/*
* The liblwm2m library is now initialized with the functions that will be in
* charge of communication
*/
lwm2mH = lwm2m_init(prv_connect_server, prv_buffer_send, &data);
if (NULL == lwm2mH)
{
fprintf(stderr, "lwm2m_init() failed\r\n");
return -1;
}
/*
* We configure the liblwm2m library with the name of the client - which shall be unique for each client -
* the number of objects we will be passing through and the objects array
*/
result = lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray);
if (result != 0)
{
fprintf(stderr, "lwm2m_configure() failed: 0x%X\r\n", result);
return -1;
}
/*
* We catch Ctrl-C signal for a clean exit
*/
signal(SIGINT, handle_sigint);
/*
* This function start your client to the LWM2M servers
*/
result = lwm2m_start(lwm2mH);
if (result != 0)
{
fprintf(stderr, "lwm2m_start() failed: 0x%X\r\n", result);
return -1;
}
fprintf(stdout, "LWM2M Client \"%s\" started on port %s.\r\nUse Ctrl-C to exit.\r\n\n", name, localPort);
/*
* We now enter in a while loop that will handle the communications from the server
*/
while (0 == g_quit)
{
struct timeval tv;
fd_set readfds;
tv.tv_sec = 60;
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(data.sock, &readfds);
/*
* This function does two things:
* - first it does the work needed by liblwm2m (eg. (re)sending some packets).
* - Secondly it adjusts the timeout value (default 60s) depending on the state of the transaction
* (eg. retransmission) and the time before the next operation
*/
result = lwm2m_step(lwm2mH, &(tv.tv_sec));
if (result != 0)
{
fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result);
return -1;
}
/*
* This part wait for an event on the socket until "tv" timed out (set
* with the precedent function)
*/
result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv);
if (result < 0)
{
if (errno != EINTR)
{
fprintf(stderr, "Error in select(): %d\r\n", errno);
}
}
else if (result > 0)
{
uint8_t buffer[MAX_PACKET_SIZE];
int numBytes;
/*
* If an event happens on the socket
*/
if (FD_ISSET(data.sock, &readfds))
{
struct sockaddr_storage addr;
socklen_t addrLen;
addrLen = sizeof(addr);
/*
* We retrieve the data received
*/
numBytes = recvfrom(data.sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen);
if (0 > numBytes)
{
fprintf(stderr, "Error in recvfrom(): %d\r\n", errno);
}
else if (0 < numBytes)
{
connection_t * connP;
connP = connection_find(data.connList, &addr, addrLen);
if (connP != NULL)
{
/*
* Let liblwm2m respond to the query depending on the context
*/
lwm2m_handle_packet(lwm2mH, buffer, numBytes, connP);
}
else
{
/*
* This packet comes from an unknown peer
*/
fprintf(stderr, "received bytes ignored!\r\n");
}
}
}
}
}
/*
* Finally when the loop is left, we unregister our client from it
*/
lwm2m_close(lwm2mH);
close(data.sock);
connection_free(data.connList);
fprintf(stdout, "\r\n\n");
return 0;
}