gateway.c 14.5 KB
Newer Older
1
2
3
4
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
5
#include <sys/stat.h>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <arpa/inet.h> //inet_addr
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <sys/time.h>
#include <libpq-fe.h>
#include <math.h>
#include <signal.h>
#include <time.h>

#include <errno.h>

#include "gateway_protocol.h"
#include "base64.h"
#include "task_queue.h"
21
#include "json.h"
22
#include "aes.h"
23

24
#define NTHREAD_MAX			10
Vladislav Rykov's avatar
Vladislav Rykov committed
25

Vladislav Rykov's avatar
Vladislav Rykov committed
26
27
28
#define TIMEDATE_LENGTH			32
#define PEND_SEND_RETRIES_MAX		5
#define GATEWAY_PROTOCOL_APP_KEY_SIZE	8
Vladislav Rykov's avatar
Vladislav Rykov committed
29
#define DEVICE_DATA_MAX_LENGTH		256
Vladislav Rykov's avatar
Vladislav Rykov committed
30

31
32
33
34
35
36
37
38
39
40
41
42
typedef struct {
	char 		gw_id[17 +1];
	char 		gw_pass[30 +1];
	uint16_t 	gw_port;
	char 		db_type[20];
	char 		db_conn_addr[15+1];
	uint16_t 	db_conn_port;
	char 		db_conn_name[16];
	char 		db_conn_user_name[32];
	char 		db_conn_user_pass[32];
} gw_conf_t;

Vladislav Rykov's avatar
Vladislav Rykov committed
43
44
typedef struct {
	uint32_t utc;
Vladislav Rykov's avatar
Vladislav Rykov committed
45
	char timedate[TIMEDATE_LENGTH];
Vladislav Rykov's avatar
Vladislav Rykov committed
46

Vladislav Rykov's avatar
Vladislav Rykov committed
47
48
	uint8_t data[DEVICE_DATA_MAX_LENGTH];
	uint8_t data_length;
Vladislav Rykov's avatar
Vladislav Rykov committed
49
50
} sensor_data_t;

Vladislav Rykov's avatar
Vladislav Rykov committed
51
typedef struct {
Vladislav Rykov's avatar
Vladislav Rykov committed
52
	gateway_protocol_conf_t gwp_conf;
Vladislav Rykov's avatar
Vladislav Rykov committed
53
54
55
56
57
58
59
	int server_desc;
	int client_desc;
	struct sockaddr_in server;
	struct sockaddr_in client;
	int sock_len;
} gcom_ch_t; // gateway communication channel

60
61
62
63
64
65
66
typedef struct {
	gcom_ch_t gch;	
	gateway_protocol_packet_type_t packet_type;
	uint8_t packet[DEVICE_DATA_MAX_LENGTH];
	uint8_t packet_length;
} gcom_ch_request_t;

67
68
69
70
static const char * gw_conf_file = "../gateway.conf";
static void process_gw_conf(json_value* value, gw_conf_t *gw_conf);
static int  read_gw_conf(const char *gw_conf_file, gw_conf_t *gw_conf);

71
void process_packet(void *request);
Vladislav Rykov's avatar
Vladislav Rykov committed
72

Vladislav Rykov's avatar
Vladislav Rykov committed
73
74
75
int send_gcom_ch(gcom_ch_t *gch, uint8_t *pck, uint8_t pck_size);
int recv_gcom_ch(gcom_ch_t *gch, uint8_t *pck, uint8_t *pck_length, uint16_t pck_size);

Vladislav Rykov's avatar
Vladislav Rykov committed
76
void packet_encode(
Vladislav Rykov's avatar
Vladislav Rykov committed
77
78
79
80
81
	const uint8_t *app_key,
	const uint8_t dev_id, 
	const gateway_protocol_packet_type_t p_type, 
	const uint8_t payload_length,
	const uint8_t *payload,
Vladislav Rykov's avatar
Vladislav Rykov committed
82
83
84
	uint8_t *packet_length,
	uint8_t *packet);
uint8_t packet_decode(
Vladislav Rykov's avatar
Vladislav Rykov committed
85
	uint8_t *app_key,
Vladislav Rykov's avatar
Vladislav Rykov committed
86
87
88
89
	uint8_t *dev_id,
	gateway_protocol_packet_type_t *ptype,
	uint8_t *payload_length,
	uint8_t *payload,
Vladislav Rykov's avatar
Vladislav Rykov committed
90
91
	const uint8_t packet_length,
	const uint8_t *packet);
Vladislav Rykov's avatar
Vladislav Rykov committed
92
93
94
95
void gateway_protocol_data_send_payload_decode(
	sensor_data_t *sensor_data, 
	const uint8_t *payload, 
	const uint8_t payload_length);
Vladislav Rykov's avatar
Vladislav Rykov committed
96

Vladislav Rykov's avatar
Vladislav Rykov committed
97
98
99
100
101
102
103
104
void gateway_protocol_mk_stat(
	gcom_ch_t *gch,
	gateway_protocol_stat_t stat,
	uint8_t *pck,
	uint8_t *pck_len);

void send_utc(gcom_ch_t *pch);

Vladislav Rykov's avatar
Vladislav Rykov committed
105
106
void gateway_protocol_checkup_callback(gateway_protocol_conf_t *gwp_conf);

Vladislav Rykov's avatar
Vladislav Rykov committed
107
108
void ctrc_handler (int sig);
static volatile uint8_t working = 1;
Vladislav Rykov's avatar
Vladislav Rykov committed
109

110
111
112
pthread_mutex_t mutex;
PGconn *conn;

Vladislav Rykov's avatar
Vladislav Rykov committed
113
int main (int argc, char **argv) {
114
115
	gw_conf_t *gw_conf = (gw_conf_t *)malloc(sizeof(gw_conf_t));
	char *db_conninfo = (char *)malloc(512);
116
	gcom_ch_t gch;
117
118
	task_queue_t *tq;

Vladislav Rykov's avatar
Vladislav Rykov committed
119
	signal(SIGINT, ctrc_handler);
120
121
122
123
124
125
126
127
128
129
130
131
	
	read_gw_conf(gw_conf_file, gw_conf);
	snprintf(db_conninfo, 512, 
			"hostaddr=%s port=%d dbname=%s user=%s password=%s", 
			gw_conf->db_conn_addr,
			gw_conf->db_conn_port,
			gw_conf->db_conn_name,
			gw_conf->db_conn_user_name,
			gw_conf->db_conn_user_pass);
							
	conn = PQconnectdb(db_conninfo);
	free(db_conninfo);
Vladislav Rykov's avatar
Vladislav Rykov committed
132
	if (PQstatus(conn) == CONNECTION_BAD) {
Vladislav Rykov's avatar
Vladislav Rykov committed
133
		fprintf(stderr,"connection to db error: %s\n", PQerrorMessage(conn));
134
		free(gw_conf);
Vladislav Rykov's avatar
Vladislav Rykov committed
135
136
137
		return EXIT_FAILURE;
	}

Vladislav Rykov's avatar
Vladislav Rykov committed
138
	if ((gch.server_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
Vladislav Rykov's avatar
Vladislav Rykov committed
139
		perror("socket creation error");
140
		free(gw_conf);
Vladislav Rykov's avatar
Vladislav Rykov committed
141
142
143
		return EXIT_FAILURE;
	}

Vladislav Rykov's avatar
Vladislav Rykov committed
144
	gch.server.sin_family 		= AF_INET;
145
	gch.server.sin_port		= htons(gw_conf->gw_port);
Vladislav Rykov's avatar
Vladislav Rykov committed
146
	gch.server.sin_addr.s_addr 	= INADDR_ANY;
Vladislav Rykov's avatar
Vladislav Rykov committed
147

Vladislav Rykov's avatar
Vladislav Rykov committed
148
	if (bind(gch.server_desc, (struct sockaddr *) &gch.server, sizeof(gch.server)) < 0) {
Vladislav Rykov's avatar
Vladislav Rykov committed
149
		perror("binding error");
150
		free(gw_conf);
Vladislav Rykov's avatar
Vladislav Rykov committed
151
152
153
		return EXIT_FAILURE;
	}

154
155
	if(!(tq = task_queue_create(NTHREAD_MAX))) {
		perror("task_queue creation error");
156
		free(gw_conf);
157
158
		return EXIT_FAILURE;
	}
159
160

	pthread_mutex_init(&mutex, NULL);
Vladislav Rykov's avatar
Vladislav Rykov committed
161
162

	gateway_protocol_set_checkup_callback(gateway_protocol_checkup_callback);
Vladislav Rykov's avatar
Vladislav Rykov committed
163
164
	
	while (working) {
165
166
167
		gcom_ch_request_t *req = (gcom_ch_request_t *)malloc(sizeof(gcom_ch_request_t));
		memset(req, 0x0, sizeof(gcom_ch_request_t));
		memcpy(&req->gch, &gch, sizeof(gcom_ch_t));
168
	
Vladislav Rykov's avatar
Vladislav Rykov committed
169
		printf("listenninig...\n");
170
		req->gch.sock_len = sizeof(req->gch.client);
Vladislav Rykov's avatar
Vladislav Rykov committed
171
		
172
173
		if (recv_gcom_ch(&req->gch, req->packet, &req->packet_length, DEVICE_DATA_MAX_LENGTH)) {
			task_queue_enqueue(tq, process_packet, req);
Vladislav Rykov's avatar
Vladislav Rykov committed
174
		} else {
175
			fprintf(stderr, "packet receive error\n");
Vladislav Rykov's avatar
Vladislav Rykov committed
176
		}
Vladislav Rykov's avatar
Vladislav Rykov committed
177
178
	}

179
	free(gw_conf);
180
	pthread_mutex_destroy(&mutex);
Vladislav Rykov's avatar
Vladislav Rykov committed
181
	close(gch.server_desc);
Vladislav Rykov's avatar
Vladislav Rykov committed
182
	PQfinish(conn);
Vladislav Rykov's avatar
Vladislav Rykov committed
183

Vladislav Rykov's avatar
Vladislav Rykov committed
184
185
186
	return EXIT_SUCCESS;
}

Vladislav Rykov's avatar
Vladislav Rykov committed
187
188
189
190
void ctrc_handler (int sig) {
	working = 0;
}

191
192
193
194
195
void process_packet(void *request) {
	gcom_ch_request_t *req = (gcom_ch_request_t *)request;
	uint8_t payload[DEVICE_DATA_MAX_LENGTH];
	uint8_t payload_length;	
	PGresult *res;
Vladislav Rykov's avatar
Vladislav Rykov committed
196
	int i;
197

Vladislav Rykov's avatar
Vladislav Rykov committed
198
199
	if (gateway_protocol_packet_decode(
		&(req->gch.gwp_conf),
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
		&(req->packet_type),
		&payload_length, payload,
		req->packet_length, req->packet))
	{
		if (req->packet_type == GATEWAY_PROTOCOL_PACKET_TYPE_TIME_REQ) {
			printf("TIME REQ received\n");
			send_utc(&(req->gch));
		} else if (req->packet_type == GATEWAY_PROTOCOL_PACKET_TYPE_DATA_SEND) {
			sensor_data_t sensor_data;
			time_t t;
			// DEVICE_DATA_MAX_LENGTH*2 {hex} + 150
			char db_query[662];

			printf("DATA SEND received\n");
			gateway_protocol_data_send_payload_decode(&sensor_data, payload, payload_length);
			
			if (sensor_data.utc == 0) {
				struct timeval tv;
				gettimeofday(&tv, NULL);
				t = tv.tv_sec;
			} else {
				t = sensor_data.utc;
			}
			
			strftime(sensor_data.timedate, TIMEDATE_LENGTH, "%d/%m/%Y %H:%M:%S", localtime(&t));
			snprintf(db_query, sizeof(db_query), 
226
				"INSERT INTO dev_%s_%d VALUES (%lu, '%s', $1)", 
Vladislav Rykov's avatar
Vladislav Rykov committed
227
				(char *)req->gch.gwp_conf.app_key, req->gch.gwp_conf.dev_id, t, sensor_data.timedate
228
229
230
231
232
233
234
235
236
			);
			
			const char *params[1];
			int paramslen[1];
			int paramsfor[1];
			params[0] = sensor_data.data;
			paramslen[0] = sensor_data.data_length;
			paramsfor[0] = 1; // format - binary

237
			pthread_mutex_lock(&mutex);
238
			res = PQexecParams(conn, db_query, 1, NULL, params, paramslen, paramsfor, 0);
239
240
			pthread_mutex_unlock(&mutex);

241
242
243
244
245
			if (PQresultStatus(res) == PGRES_COMMAND_OK) {
				PQclear(res);

				snprintf(db_query, sizeof(db_query),
					 "SELECT * FROM pend_msgs WHERE app_key='%s' and dev_id = %d and ack = False", 
Vladislav Rykov's avatar
Vladislav Rykov committed
246
					(char *)req->gch.gwp_conf.app_key, req->gch.gwp_conf.dev_id
247
				);
248
249
				
				pthread_mutex_lock(&mutex);
250
				res = PQexec(conn, db_query);
251
252
				pthread_mutex_unlock(&mutex);
				
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
				if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res)) {
					gateway_protocol_mk_stat(
						&(req->gch), 
						GATEWAY_PROTOCOL_STAT_ACK_PEND,
						req->packet, &(req->packet_length));
					printf("ACK_PEND prepared\n");
				} else {
					gateway_protocol_mk_stat(
						&(req->gch), 
						GATEWAY_PROTOCOL_STAT_ACK,
						req->packet, &(req->packet_length));
					printf("ACK prepared\n");
				}
				
				send_gcom_ch(&(req->gch), req->packet, req->packet_length);
			} else {
				fprintf(stderr, "database error : %s\n", PQerrorMessage(conn));
			}
			PQclear(res);
		} else if (req->packet_type == GATEWAY_PROTOCOL_PACKET_TYPE_PEND_REQ) {
			char db_query[200];
			snprintf(db_query, sizeof(db_query),
				 "SELECT * FROM pend_msgs WHERE app_key = '%s' AND dev_id = %d AND ack = False", 
Vladislav Rykov's avatar
Vladislav Rykov committed
276
				(char *)req->gch.gwp_conf.app_key, req->gch.gwp_conf.dev_id
277
			);
278
			pthread_mutex_lock(&mutex);
279
			res = PQexec(conn, db_query);
280
			pthread_mutex_unlock(&mutex);
281
282
283
284
285
286
287
288
289
290
291
292
293
294
			
			if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res)) {
				char msg_cont[150];
				strncpy(msg_cont, PQgetvalue(res, 0, 2), sizeof(msg_cont));
				printf("PEND_SEND prepared : %s\n", msg_cont);
				PQclear(res);
			
				base64_decode(msg_cont, strlen(msg_cont)-1, payload);
				payload_length = BASE64_DECODE_OUT_SIZE(strlen(msg_cont));
				printf("prepared to send %d bytes : %s\n", payload_length, payload);
				
				// send the msg until ack is received
				uint8_t received_ack = 0;
				uint8_t pend_send_retries = PEND_SEND_RETRIES_MAX;
Vladislav Rykov's avatar
Vladislav Rykov committed
295
296
				gateway_protocol_packet_encode(
					&(req->gch.gwp_conf),
297
298
299
					GATEWAY_PROTOCOL_PACKET_TYPE_PEND_SEND,
					payload_length, payload,
					&(req->packet_length), req->packet);
300
301
				do {
					send_gcom_ch(&(req->gch), req->packet, req->packet_length);
302
303
304
305
306
307
308
309
310
311
312
					
					// 300 ms
					usleep(300000);

					pthread_mutex_lock(&mutex);
					res = PQexec(conn, db_query);
					pthread_mutex_unlock(&mutex);
					
					if (PQresultStatus(res) == PGRES_TUPLES_OK) {
						if (!PQntuples(res) || strcmp(PQgetvalue(res, 0, 2), msg_cont)) {
							received_ack = 1;
313
314
						}
					}
315
316
					PQclear(res);
					printf("received_ack = %d, retries = %d\n", received_ack, pend_send_retries);
317
318
319
320
321
322
323
324
325
				} while (!received_ack && pend_send_retries--);
			} else {
				gateway_protocol_mk_stat(
					&(req->gch),
					GATEWAY_PROTOCOL_STAT_NACK,
					req->packet, &(req->packet_length));
				
				send_gcom_ch(&(req->gch), req->packet, req->packet_length);
				
Vladislav Rykov's avatar
Vladislav Rykov committed
326
				printf("nothing for app %s dev %d\n", (char *)req->gch.gwp_conf.app_key, req->gch.gwp_conf.dev_id);
327
			}
328
329
330
331
332
333
		} else if (req->packet_type == GATEWAY_PROTOCOL_PACKET_TYPE_STAT) {
			// TODO change to ACK_PEND = 0x01
			if (payload[0] == 0x00) {
				char db_query[200];
				snprintf(db_query, sizeof(db_query),
					 "SELECT * FROM pend_msgs WHERE app_key = '%s' AND dev_id = %d AND ack = False", 
Vladislav Rykov's avatar
Vladislav Rykov committed
334
					(char *)req->gch.gwp_conf.app_key, req->gch.gwp_conf.dev_id
335
336
337
338
339
340
341
				);
				pthread_mutex_lock(&mutex);
				res = PQexec(conn, db_query);
				pthread_mutex_unlock(&mutex);
				if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res)) {
					snprintf(db_query, sizeof(db_query),
						"UPDATE pend_msgs SET ack = True WHERE app_key = '%s' AND dev_id = %d AND msg = '%s'",
Vladislav Rykov's avatar
Vladislav Rykov committed
342
						(char *)req->gch.gwp_conf.app_key, req->gch.gwp_conf.dev_id, PQgetvalue(res, 0, 2)
343
344
345
346
347
348
349
350
351
352
353
354
355
					);
					PQclear(res);
					pthread_mutex_lock(&mutex);
					res = PQexec(conn, db_query);
					pthread_mutex_unlock(&mutex);
					if (PQresultStatus(res) == PGRES_COMMAND_OK) {
						printf("pend_msgs updated\n");
					} else {
						fprintf(stderr, "database error : %s\n", PQerrorMessage(conn));
					}
				}
				PQclear(res);
			}
356
357
358
359
360
361
362
363
		} else {
			gateway_protocol_mk_stat(
				&(req->gch),
				GATEWAY_PROTOCOL_STAT_NACK,
				req->packet, &(req->packet_length));
			
			send_gcom_ch(&(req->gch), req->packet, req->packet_length);
				
364
			fprintf(stderr, "packet type error : %02X\n", req->packet_type);
365
366
367
368
		}
	} else {
		fprintf(stderr, "payload decode error\n");
	}
369
370
	
	free(req);
371
372
}

Vladislav Rykov's avatar
Vladislav Rykov committed
373
void gateway_protocol_data_send_payload_decode(
Vladislav Rykov's avatar
Vladislav Rykov committed
374
375
376
377
	sensor_data_t *sensor_data, 
	const uint8_t *payload, 
	const uint8_t payload_length) 
{
Vladislav Rykov's avatar
Vladislav Rykov committed
378
379
380
381
382
	uint8_t p_len = 0;

	memcpy(&sensor_data->utc, &payload[p_len], sizeof(sensor_data->utc));
	p_len += sizeof(sensor_data->utc);

Vladislav Rykov's avatar
Vladislav Rykov committed
383
384
	memcpy(sensor_data->data, &payload[p_len], payload_length - p_len);
	sensor_data->data_length = payload_length - p_len;
Vladislav Rykov's avatar
Vladislav Rykov committed
385
386
}

Vladislav Rykov's avatar
Vladislav Rykov committed
387
388
389
390
391
392
void gateway_protocol_mk_stat(
	gcom_ch_t *gch,
	gateway_protocol_stat_t stat,
	uint8_t *pck,
	uint8_t *pck_len)
{
Vladislav Rykov's avatar
Vladislav Rykov committed
393
394
	gateway_protocol_packet_encode(
		&(gch->gwp_conf),
Vladislav Rykov's avatar
Vladislav Rykov committed
395
		GATEWAY_PROTOCOL_PACKET_TYPE_STAT,
Vladislav Rykov's avatar
Vladislav Rykov committed
396
		1, (uint8_t *)&stat,
Vladislav Rykov's avatar
Vladislav Rykov committed
397
		pck_len, pck);
Vladislav Rykov's avatar
Vladislav Rykov committed
398
399
}

Vladislav Rykov's avatar
Vladislav Rykov committed
400
401


Vladislav Rykov's avatar
Vladislav Rykov committed
402
void send_utc(gcom_ch_t *gch) {
Vladislav Rykov's avatar
Vladislav Rykov committed
403
	uint8_t buf[50];
Vladislav Rykov's avatar
Vladislav Rykov committed
404
405
406
407
408
	uint8_t buf_len = 0;
	struct timeval tv;
				
	gettimeofday(&tv, NULL);
				
Vladislav Rykov's avatar
Vladislav Rykov committed
409
410
	gateway_protocol_packet_encode (
		&(gch->gwp_conf),
Vladislav Rykov's avatar
Vladislav Rykov committed
411
		GATEWAY_PROTOCOL_PACKET_TYPE_TIME_SEND,
Vladislav Rykov's avatar
Vladislav Rykov committed
412
		sizeof(uint32_t), (uint8_t *)&tv.tv_sec,
Vladislav Rykov's avatar
Vladislav Rykov committed
413
414
415
416
417
418
		&buf_len, buf
	);
					
	send_gcom_ch(gch, buf, buf_len);
}

Vladislav Rykov's avatar
Vladislav Rykov committed
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
void gateway_protocol_checkup_callback(gateway_protocol_conf_t *gwp_conf) {
	PGresult *res;
	char db_query[200];
	int i;
	snprintf(db_query, sizeof(db_query), 
		"SELECT secure_key, secure FROM applications WHERE app_key = '%s'", (char *)gwp_conf->app_key
	);
	pthread_mutex_lock(&mutex);
	res = PQexec(conn, db_query);
	pthread_mutex_unlock(&mutex);

	if ((PQresultStatus(res) == PGRES_TUPLES_OK) && PQntuples(res)) {
		base64_decode(PQgetvalue(res, 0, 0), strlen(PQgetvalue(res, 0, 0))-1, gwp_conf->secure_key);
		gwp_conf->secure = PQgetvalue(res, 0, 1)[0] == 't';
	} else {
		perror("gateway_protocol_checkup_callback error");
	}
	PQclear(res);
}

Vladislav Rykov's avatar
Vladislav Rykov committed
439
int send_gcom_ch(gcom_ch_t *gch, uint8_t *pck, uint8_t pck_size) {
440
	int ret;
Vladislav Rykov's avatar
Vladislav Rykov committed
441
	
442
	if ((ret = sendto(gch->server_desc, (char *)pck, pck_size, 0, (struct sockaddr *)&gch->client, gch->sock_len)) < 0) {
Vladislav Rykov's avatar
Vladislav Rykov committed
443
444
		perror("sendto error");
	}
445

Vladislav Rykov's avatar
Vladislav Rykov committed
446
447
448
449
	return ret;
}

int recv_gcom_ch(gcom_ch_t *gch, uint8_t *pck, uint8_t *pck_length, uint16_t pck_size) {
450
	int ret, i;
451
	if ((ret = recvfrom(gch->server_desc, (char *)pck, pck_size, MSG_WAITALL, (struct sockaddr *)&gch->client, &gch->sock_len)) < 0) {
Vladislav Rykov's avatar
Vladislav Rykov committed
452
		perror("socket receive error");
453
454
	} else {
		*pck_length = ret;
455
		
Vladislav Rykov's avatar
Vladislav Rykov committed
456
	}
457

458
	return ret;
Vladislav Rykov's avatar
Vladislav Rykov committed
459
}
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526


static void process_gw_conf(json_value* value, gw_conf_t *gw_conf) {
	json_value *pv = value;

	strncpy(gw_conf->gw_id, pv->u.object.values[0].value->u.string.ptr, sizeof(gw_conf->gw_id));
	strncpy(gw_conf->gw_pass, pv->u.object.values[1].value->u.string.ptr, sizeof(gw_conf->gw_pass));
	gw_conf->gw_port = pv->u.object.values[2].value->u.integer;
	strncpy(gw_conf->db_type, pv->u.object.values[3].value->u.string.ptr, sizeof(gw_conf->db_type));
	
	pv = pv->u.object.values[4].value;
	
	strncpy(gw_conf->db_conn_addr, pv->u.object.values[0].value->u.string.ptr, sizeof(gw_conf->db_conn_addr));
	gw_conf->db_conn_port = pv->u.object.values[1].value->u.integer;
	strncpy(gw_conf->db_conn_name, pv->u.object.values[2].value->u.string.ptr, sizeof(gw_conf->db_conn_name));
	strncpy(gw_conf->db_conn_user_name, pv->u.object.values[3].value->u.string.ptr, sizeof(gw_conf->db_conn_user_name));
	strncpy(gw_conf->db_conn_user_pass, pv->u.object.values[4].value->u.string.ptr, sizeof(gw_conf->db_conn_user_pass));
}

static int read_gw_conf(const char *gw_conf_file, gw_conf_t *gw_conf) {
	struct stat filestatus;
	FILE *fp;
	char *file_contents;
	json_char *json;
	json_value *jvalue;

	if (stat(gw_conf_file, &filestatus)) {
		fprintf(stderr, "File %s not found.", gw_conf_file);
		return 1;
	}
	file_contents = (char *)malloc(filestatus.st_size);
	if (!file_contents) {
		fprintf(stderr, "Memory error allocating %d bytes.", filestatus.st_size);
		return 1;
	}
	fp = fopen(gw_conf_file, "rt");
	if (!fp) {
		fprintf(stderr, "Unable to open %s.", gw_conf_file);
		fclose(fp);
		free(file_contents);
		return 1;
	}
	if (fread(file_contents, filestatus.st_size, 1, fp) != 1) {
		fprintf(stderr, "Unable to read %s.", gw_conf_file);
		fclose(fp);
		free(file_contents);
		return 1;
	}
	fclose(fp);
	
	printf("%s\n", file_contents);
	
	json = (json_char *)file_contents;
	jvalue = json_parse(json, filestatus.st_size);
	if (!jvalue) {
		perror("Unable to parse json.");
		free(file_contents);
		return 1;
	}
	
	process_gw_conf(jvalue, gw_conf);

	json_value_free(jvalue);
	free(file_contents);
	
	return 0;
}