Skip to content
Snippets Groups Projects
Commit cd11cbe4 authored by clohr's avatar clohr
Browse files

Premiere tentative avec la librairie json-c

git-svn-id: https://redmine.imt-atlantique.fr/svn/xaal/xAAL_C/tags/testing_jansson@430 b32b6428-25c9-4566-ad07-03861ab6144f
parent b86e9f5b
Branches
No related tags found
No related merge requests found
PROG = dummyLamp
CFLAGS = -Wall
LDFLAGS = -ljansson -luuid
all:$(PROG)
test: $(PROG)
./$(PROG) -a 224.0.29.200 -p 1234
test6: $(PROG)
./$(PROG) -a ff15::1dc8 -p 1234
.PHONY: all test test6
- Dependencies
packages: uuid-dev libjansson-dev
- Notes about the jansson lib [Mon Oct 20 2014, Christophe Lohr]
. confusion between 'key-value pairs' vs. 'just values'
. risks of memory leaks
eg. after a:
if ( json_equal(myobject, json_string("foobar")) { ... }
what appends to memory used to build json_string("foobar")?
File added
/* xAAL dummy basic lamp
* (c) 2014 Christophe Lohr <christophe.lohr@telecom-bretagne.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#include <jansson.h>
#include <uuid/uuid.h>
#define ALIVE_PERIOD 5
#define BUF_SIZE 65536
#define xAAL_VERSION "0.3"
FILE *error_log;
/* Join the xAAL bus */
int join_bus(const char *addr, const char *port, int ttl, int mcast_loop,
struct sockaddr_storage *bus_addr, socklen_t *bus_addrlen) {
int s, sfd;
int one = 1;
struct ip_mreqn mreqn;
struct ipv6_mreq mreq6;
struct addrinfo hints;
struct addrinfo *result, *rp;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
//hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
s = getaddrinfo(addr, port, &hints, &result);
if (s != 0) {
fprintf(error_log, "getaddrinfo: %s\n", gai_strerror(s));
return -1;
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */ /*copy-paste from the man page*/
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
fprintf(error_log, "SO_REUSEADDR: %s\n", strerror(errno));
close(sfd);
continue;
}
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
fprintf(error_log, "Trying port %s: %s\n", port, strerror(errno));
close(sfd);
}
if (rp == NULL) { /* No address succeeded */
fprintf(error_log, "Could not bind\n");
return -1;
}
/* Now, join the group */
switch (rp->ai_family) {
case AF_INET:
memcpy(&mreqn.imr_multiaddr.s_addr, &(((struct sockaddr_in*)(rp->ai_addr))->sin_addr), sizeof(struct in_addr));
mreqn.imr_address.s_addr = INADDR_ANY;
mreqn.imr_ifindex = 0;
if ( setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn)) == -1) {
fprintf(error_log, "Could not join the multicast group: %s", strerror(errno));
return -1;
}
if ( setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1) {
fprintf(error_log, "Could not set TTL to %d: %s", ttl, strerror(errno));
return -1;
}
if ( setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_LOOP, &mcast_loop, sizeof(mcast_loop)) == -1) {
fprintf(error_log, "Could not %s multicast loop: %s", mcast_loop?"enable":"disable", strerror(errno));
return -1;
}
break;
case AF_INET6:
memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6*)(rp->ai_addr))->sin6_addr), sizeof(struct in6_addr));
mreq6.ipv6mr_interface = 0;
if ( setsockopt(sfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) == -1) {
fprintf(error_log, "Could not join the multicast group: %s", strerror(errno));
return -1;
}
if ( setsockopt(sfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) == -1) {
fprintf(error_log, "Could not set TTL to %d: %s", ttl, strerror(errno));
return -1;
}
if ( setsockopt(sfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &mcast_loop, sizeof(mcast_loop)) == -1) {
fprintf(error_log, "Could not %s multicast loop: %s", mcast_loop?"enable":"disable", strerror(errno));
return -1;
}
break;
default:
fprintf(error_log, "Unknown protocol %d\n", rp->ai_family);
return -1;
}
memcpy(bus_addr, rp->ai_addr, rp->ai_addrlen);
*bus_addrlen = rp->ai_addrlen;
freeaddrinfo(result); /* No longer needed */
return sfd;
}
/* Manage header */
/* Return true if parsing success */
int parseHeader(const json_t *jroot,
json_t **jversion_, json_t **jsource_, json_t **jtarget_,
json_t **jmsgType_, json_t **jdevType_, json_t **jaction_,
json_t **jcipher_, json_t **jsignature_, json_t **jtimestamp_) {
json_t *jheader;
int r = 1;
if (!json_is_object(jroot)) {
fprintf(error_log, "Invalid xAAL message\n");
r = 0;
}
jheader = json_object_get(jroot, "header");
if ( !json_is_object(jheader) ) {
fprintf(error_log, "Invalid xAAL header\n");
r = 0;
}
*jversion_ = json_object_get(jheader, "version");
if ( !json_is_string(*jversion_) ) {
fprintf(error_log, "Invalid 'version' in xAAL header\n");
r = 0;
}
//printf("version: %s\n", json_string_value(*jversion_));
*jsource_ = json_object_get(jheader, "source");
if ( !json_is_string(*jsource_) ) {
fprintf(error_log, "Invalid 'source' in xAAL header\n");
r = 0;
}
//printf("source: %s\n", json_string_value(*jsource_));
*jtarget_ = json_object_get(jheader, "target");
if ( !json_is_string(*jtarget_) ) {
fprintf(error_log, "Invalid 'target' in xAAL header\n");
r = 0;
}
//printf("target: %s\n", json_string_value(*jtarget_));
*jdevType_ = json_object_get(jheader, "devType");
if ( !json_is_string(*jdevType_) ) {
fprintf(error_log, "Invalid 'devType' in xAAL header\n");
r = 0;
}
//printf("devType: %s\n", json_string_value(*jdevType_));
*jmsgType_ = json_object_get(jheader, "msgType");
if ( !json_is_string(*jmsgType_) ) {
fprintf(error_log, "Invalid 'msgType' in xAAL header\n");
r = 0;
}
*jaction_ = json_object_get(jheader, "action");
if ( !json_is_string(*jaction_) ) {
fprintf(error_log, "Invalid 'action' in xAAL header\n");
r = 0;
}
*jcipher_ = json_object_get(jheader, "cipher");
if ( !json_is_string(*jcipher_) ) {
fprintf(error_log, "Invalid 'cipher' in xAAL header\n");
r = 0;
}
*jsignature_ = json_object_get(jheader, "signature");
if ( !json_is_string(*jsignature_) ) {
fprintf(error_log, "Invalid 'signature' in xAAL header\n");
r = 0;
}
*jtimestamp_ = json_object_get(jheader, "timestamp");
if ( *jtimestamp_ && !json_is_string(*jtimestamp_) ) { /* optionnal */
fprintf(error_log, "Invalid 'timestamp' in xAAL header\n");
r = 0;
}
if ( !r )
json_dumpf(jroot, error_log, JSON_INDENT(1));
return r;
}
/* Send alive messages */
/* Return true if success */
int sendAlive(int sfd, struct sockaddr_storage *bus_addr, socklen_t bus_addrlen,
json_t *my_uuid, json_t *my_devType) {
json_t *jmsg, *jheader ;
char *msg;
int r = 0;
jheader = json_object();
jmsg = json_object();
if ( ( json_object_set(jheader, "version", json_string(xAAL_VERSION)) == 0 )
&& ( json_object_set(jheader, "target", json_string("00000000-0000-0000-0000-000000000000")) == 0 )
&& ( json_object_set(jheader, "source", my_uuid) == 0 )
&& ( json_object_set(jheader, "devType", my_devType) == 0 )
&& ( json_object_set(jheader, "msgType", json_string("notify")) == 0 )
&& ( json_object_set(jheader, "action", json_string("alive")) == 0 )
&& ( json_object_set(jheader, "cipher", json_string("")) == 0 )
&& ( json_object_set(jheader, "signature", json_string("")) == 0 )
&& ( json_object_set(jmsg, "header", jheader) == 0 )
&& ( (msg = json_dumps(jmsg, JSON_COMPACT)) )
) {
if ( sendto(sfd, msg, strlen(msg), 0, (struct sockaddr *)bus_addr, bus_addrlen) == -1)
fprintf(error_log, "Error while sending alive: %s\n", strerror(errno));
else
r = 1;
}
json_decref(jheader);
json_decref(jmsg);
return r;
}
struct alive_context_t {
int sfd;
struct sockaddr_storage *bus_addr;
socklen_t bus_addrlen;
json_t *my_uuid;
json_t *my_devType;
} alive_context;
void alive_sender(int sig) {
if ( !sendAlive(alive_context.sfd, alive_context.bus_addr, alive_context.bus_addrlen,
alive_context.my_uuid, alive_context.my_devType) )
fprintf(error_log, "Could not send spontaneous alive notification.\n");
alarm(ALIVE_PERIOD);
}
/* sendDescription */
/* Return true if success */
int sendDescription(int sfd, struct sockaddr_storage *bus_addr, socklen_t bus_addrlen,
json_t *my_uuid, json_t *my_devType, json_t *jsender) {
json_t *jmsg, *jheader, *jbody;
char *msg;
int r = 0;
jheader = json_object();
jbody = json_object();
jmsg = json_object();
if ( ( json_object_set(jheader, "version", json_string(xAAL_VERSION)) == 0 )
&& ( json_object_set(jheader, "target", jsender) == 0 )
&& ( json_object_set(jheader, "source", my_uuid) == 0 )
&& ( json_object_set(jheader, "devType", my_devType) == 0 )
&& ( json_object_set(jheader, "msgType", json_string("reply")) == 0 )
&& ( json_object_set(jheader, "action", json_string("sendDescription")) == 0 )
&& ( json_object_set(jheader, "cipher", json_string("")) == 0 )
&& ( json_object_set(jheader, "signature", json_string("")) == 0 )
&& ( json_object_set(jmsg, "header", jheader) == 0 )
&& ( json_object_set(jbody, "vendorId", json_string("Team IHSEV")) == 0 )
&& ( json_object_set(jbody, "productId", json_string("Dummy basic lamp")) == 0 )
&& ( json_object_set(jbody, "version", json_string("0.1")) == 0 )
&& ( json_object_set(jbody, "parent", json_string("00000000-0000-0000-0000-000000000000")) == 0 )
&& ( json_object_set(jmsg, "body", jbody) == 0 )
&& ( (msg = json_dumps(jmsg, JSON_COMPACT)) )
) {
if ( sendto(sfd, msg, strlen(msg), 0, (struct sockaddr *)bus_addr, bus_addrlen) == -1 )
fprintf(error_log, "Error while sending dêscription: %s\n", strerror(errno));
else
r = 1;
}
json_decref(jheader);
json_decref(jbody);
json_decref(jmsg);
return r;
}
/* main */
int main(int argc, char **argv) {
ssize_t nread;
char buf[BUF_SIZE];
json_t *jroot;
json_error_t jerror;
int sfd;
struct sockaddr_storage bus_addr;
socklen_t bus_addrlen;
int opt;
char *addr=NULL, *port=NULL;
uuid_t uuid;
int ttl = -1;
int arg_error = 0;
int attribute_light = 0;
json_t *my_uuid, *my_devType, *bcast, *any_any;
struct sigaction act_alarm;
uuid_clear(uuid);
/* Parse cmdline arguments */
while ((opt = getopt(argc, argv, "a:p:t:u:")) != -1) {
switch (opt) {
case 'a':
addr = optarg;
break;
case 'p':
port = optarg;
break;
case 't':
ttl = atoi(optarg);
break;
case 'u':
if ( uuid_parse(optarg, uuid) == -1 ) {
fprintf(stderr, "Warning: invalid uuid:%s\n", optarg);
uuid_clear(uuid);
}
break;
default: /* '?' */
arg_error = 1;
}
}
if (optind < argc) {
fprintf(stderr, "Unknown argument %s\n", argv[optind]);
arg_error = 1;
}
if (arg_error) {
fprintf(stderr, "Usage: %s -a <addr> -p <port> [-t <ttl>] [-u <uuid>]\n",
argv[0]);
exit(EXIT_FAILURE);
}
/* Join the xAAL bus */
error_log = stderr;
sfd = join_bus(addr, port, ttl, 1, &bus_addr, &bus_addrlen);
if (sfd == -1)
exit(EXIT_FAILURE);
/* Generate device address if needed */
if ( uuid_is_null(uuid) ) {
char *uuid_str = malloc(37);
uuid_generate(uuid);
uuid_unparse(uuid, uuid_str);
printf("Device: %s\n", uuid_str);
my_uuid = json_string(uuid_str);
}
/* Init some json objects */
my_devType = json_string("lamp.basic");
bcast = json_string("'00000000-0000-0000-0000-000000000000");
any_any = json_string("any.any");
/* Manage alive notifications */
alive_context.sfd = sfd;
alive_context.bus_addr = &bus_addr;
alive_context.bus_addrlen = bus_addrlen;
alive_context.my_uuid = my_uuid;
alive_context.my_devType = my_devType;
act_alarm.sa_handler = alive_sender;
act_alarm.sa_flags = ~SA_RESETHAND;
sigemptyset(&act_alarm.sa_mask);
sigaction(SIGALRM, &act_alarm, NULL);
alarm(ALIVE_PERIOD);
if ( !sendAlive(sfd, &bus_addr, bus_addrlen, my_uuid, my_devType) )
fprintf(error_log, "Could not send initial alive notification.\n");
/* Main loop */
for (;;) {
/* Show lamp */
if (attribute_light)
printf(" (O) On \r");
else
printf(" . Off\r");
fflush(stdout);
/* Recive a message */
nread = recvfrom(sfd, buf, BUF_SIZE, 0, NULL, NULL);
if (nread == -1)
continue; /* Ignore failed request */
/* Use the Jansson library */
jroot = json_loadb(buf, nread, 0, &jerror);
if ( jroot == NULL ) {
fprintf(error_log, "JSON error on line %d: %s\n", jerror.line, jerror.text);
write(STDOUT_FILENO, buf, nread);
}
else {
/* Manage message */
json_t *jversion, *jsource, *jtarget, *jmsgType, *jdevType, *jaction,
*jcipher, *jsignature, *jtimestamp;
if ( parseHeader(jroot, &jversion, &jsource, &jtarget, &jmsgType, &jdevType,
&jaction, &jcipher, &jsignature, &jtimestamp)
&& (json_equal(jtarget, my_uuid) || json_equal(jtarget, bcast))
&& (json_equal(jdevType, my_devType) || json_equal(jdevType, any_any))
&& json_equal(jversion, json_string(xAAL_VERSION))
&& json_equal(jmsgType, json_string("request")) ) {
/* ok, this is a request for me */
if ( json_equal(jaction, json_string("isAlive")) ) {
if ( !sendAlive(sfd, &bus_addr, bus_addrlen, my_uuid, my_devType) )
fprintf(error_log, "Could not reply to isAlive\n");
} else if ( json_equal(jaction, json_string("getDescription")) ) {
if ( !sendDescription(sfd, &bus_addr, bus_addrlen, my_uuid, my_devType,
jsource) )
fprintf(error_log, "Could not reply to getDescription\n");
} else if ( json_equal(jaction, json_string("getAttributes")) ) {
} else if ( json_equal(jaction, json_string("getBusConfig")) ) {
} else if ( json_equal(jaction, json_string("getCiphers")) ) {
} else if ( json_equal(jaction, json_string("getCiphers")) ) {
} else if ( json_equal(jaction, json_string("on")) ) {
attribute_light = 1;
} else if ( json_equal(jaction, json_string("off")) ) {
attribute_light = 0;
}
}
json_decref(jroot);
}
}
}
ADDR=224.0.29.200
PORT=1234
if [ -z "$1" ]; then
echo "Erro: no uuid!" >&2
exit 1
fi
socat UDP-DATAGRAM:$ADDR:$PORT,bind=:$PORT,ip-add-membership=$ADDR:0,reuseaddr STDIO << EOF
{"header": {"target": "$1", "source": "$(uuidgen)", "devType": "lamp.basic", "version": "0.3", "msgType": "request", "signature": "", "action": "off", "cipher": ""}}
EOF
ADDR=224.0.29.200
PORT=1234
if [ -z "$1" ]; then
echo "Erro: no uuid!" >&2
exit 1
fi
socat UDP-DATAGRAM:$ADDR:$PORT,bind=:$PORT,ip-add-membership=$ADDR:0,reuseaddr STDIO << EOF
{"header": {"target": "$1", "source": "$(uuidgen)", "devType": "lamp.basic", "version": "0.3", "msgType": "request", "signature": "", "action": "on", "cipher": ""}}
EOF
......@@ -4,9 +4,9 @@ LDFLAGS=-ljansson
all:dumper
test: dumper
./dumper 224.0.10.10 1234
./dumper 224.0.29.200 1234
test6: dumper
./dumper ff15::0a0a 1234
./dumper ff15::1dc8 1234
.PHONY: all test test6
/* xAAL bus dumper
* (c) 2014 Christophe Lohr <christophe.lohr@telecom-bretagne.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
......
# ADDR="[ff15::0a0a]"
ADDR="224.0.10.10"
# ADDR="[ff15::1dc8]"
ADDR="224.0.29.200"
PORT="1234"
TTL="2"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment