Subversion Repositories heating

Rev

Rev 48 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 andreas 1
/*
2
 * Copyright (C) 2015 by Andreas Theofilu. All rights reserved!
3
 *
4
 * All rights reserved. No warranty, explicit or implicit, provided.
5
 *
6
 * NOTICE:  All information contained herein is, and remains
7
 * the property of Andreas Theofilu and his suppliers, if any.
8
 * The intellectual and technical concepts contained
9
 * herein are proprietary to Andreas Theofilu and its suppliers and
10
 * may be covered by European and Foreign Patents, patents in process,
11
 * and are protected by trade secret or copyright law.
12
 * Dissemination of this information or reproduction of this material
13
 * is strictly forbidden unless prior written permission is obtained
14
 * from Andreas Theofilu.
15
 * 
16
 * Author: Andreas Theofilu <andreas@theosys.at>
17
 */
18
#include <iostream>
19
#include <fstream>
20
#include <string>
9 andreas 21
#include <vector>
3 andreas 22
#include <cstdio>
23
#include <cstring>
24
#include <ctime>
25
#include <cstdlib>
26
#include <csignal>
27
#include <syslog.h>
28
#include <cerrno>
29
#include <unistd.h>
30
#include <pthread.h>
31
#include <sys/stat.h>
32
#include <sys/types.h>
33
#include <sys/socket.h>
34
#include <fcntl.h>
35
#include <netdb.h>
36
#include <arpa/inet.h>
37
#include <netinet/in.h>
38
#include <sys/ioctl.h>
39
#include <sys/param.h>
40
#include <pwd.h>
41
#include <grp.h>
54 andreas 42
#include <bcm2835.h>
18 andreas 43
#include <sys/resource.h>
3 andreas 44
#include "config.h"
5 andreas 45
#include "heating.h"
3 andreas 46
 
47
struct SOCKETS
48
{
49
	int sockfd;
50
	int newfd;
51
	char ip[256];
52
};
53
 
54
void daemon_start (int ignsigcld);
55
void sig_handler(int sig);
56
void changeToUser(const char *usr, const char *grp);
57
void sig_child ();
58
 
59
void *pthr_parser(void *pV_data);
5 andreas 60
void *pthr_Heat(void * pV_data);
3 andreas 61
void *processCommands(void *pV_data);
62
int parseCommand(int s1, char *buf);
63
 
5 andreas 64
static pthread_t pthr_pars, pthr_heat, pthr_process;
65
static pthread_mutex_t fastmutex_proc = PTHREAD_MUTEX_INITIALIZER;
66
 
67
heating *heat;
3 andreas 68
struct SOCKETS soc;				// The network sockets to be passed to a thread
69
 
70
int main(int argc, char **argv) 
71
{
18 andreas 72
struct rlimit core_limits;
73
 
5 andreas 74
	heat = nullptr;
54 andreas 75
	bcmInitialized = false;
76
 
3 andreas 77
	// First read config file and check if it was ok
78
	config *cfg = new config();
79
 
80
	if (!cfg->is_initialized())
81
	{
82
		std::cerr << "Error reading config file! Make sure there is one and that it is readable by root!" << std::endl;
5 andreas 83
		delete (cfg);
84
		return 1;
3 andreas 85
	}
86
 
87
	CONFIGURE configs = cfg->getConfig();
54 andreas 88
	// We must initialize the BCM2835 library here, as long as we are root!
89
	if (!bcm2835_init())
90
		syslog(LOG_WARNING, "Error initializing the BCM2835 library! The GPIO ports will not function.");
91
	else
92
		bcmInitialized = true;
93
 
3 andreas 94
	// Daemonize and run in background
95
	daemon_start(0);
96
	changeToUser(configs.user, configs.group);
18 andreas 97
	// core dumps may be disallowed by parent of this process; change that
98
	if (Configure.debug)
99
	{
100
		core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
101
		setrlimit(RLIMIT_CORE, &core_limits);
102
	}
3 andreas 103
	// We no longer need the configuration data here, so we remove them.
104
	delete (cfg);
105
	// Now start our Thread
106
	if (pthread_create(&pthr_pars, NULL, pthr_parser, (void *)0) != 0)
107
	{
108
		syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
5 andreas 109
		return 2;
3 andreas 110
	}
111
 
112
	while (1)
113
		sleep(3600);
114
 
115
	return 0;
116
}
5 andreas 117
 
118
/*
119
 * Detach application from console and make it a daemon.
120
 */
121
void daemon_start (int ignsigcld)
122
{
123
	int childpid, fd;
124
	char hv0[128];
125
 
126
	if (getpid () == 1)
127
		goto out;
128
 
129
	#ifdef SIGTTOU
130
	signal (SIGTTOU, SIG_IGN);
131
	#endif
132
	#ifdef SIGTTIN
133
	signal (SIGTTIN, SIG_IGN);
134
	#endif
135
	#ifdef SIGTSTP
136
	signal (SIGTSTP, SIG_IGN);
137
	#endif
138
 
139
	if ((childpid = fork ()) < 0)
140
		fprintf (stderr, "Can't fork this child\n");
141
	else if (childpid > 0)
142
		exit (0);
143
 
144
	if (setpgrp () == -1)
145
		fprintf (stderr, "Can't change process group\n");
146
 
147
	signal (SIGHUP, SIG_IGN);
148
 
149
	if ((childpid = fork ()) < 0)
150
		syslog (LOG_DAEMON, "Can't fork second child");
151
	else if (childpid > 0)
152
		exit (0);            /* first child */
153
 
154
	/* second child */
155
out:
156
	for (fd = 0; fd < NOFILE; fd++)
157
		close (fd);
158
 
159
	errno = 0;
160
	chdir ("/");
161
	umask (0);
162
 
163
	if (ignsigcld)
164
		signal (SIGCLD, SIG_IGN);
165
 
166
	// Define a signal handler for terminate signals
167
	if (signal(SIGTERM, sig_handler) == SIG_ERR)
168
		syslog(LOG_WARNING,"Can't catch signal SIGTERM!");
169
 
170
	// Create PID file
171
	if ((fd = open(Configure.pidfile, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
172
	{
173
		syslog(LOG_WARNING,"Can't create PID file %s: %s",Configure.pidfile, strerror(errno));
174
		return;
175
	}
176
 
177
	sprintf(hv0, "%d", getpid());
178
	write(fd, hv0, strlen(hv0));
179
	close(fd);
180
}
181
 
182
void sig_child ()
183
{
184
#if defined(BSD) && !defined(sinix) && !defined(Linux)
185
	int pid;
186
	union wait status;
187
 
188
	while ((pid = wait3 (&status, WNOHANG, (struct rusage *)0)) > 0)
189
		;
190
#endif
191
}
192
 
193
/*
194
 * This is the signal handler who disconnects from network and terminates
195
 * this daemon
196
 */
197
void sig_handler(int sig)
198
{
199
	if (sig == SIGTERM || sig == SIGKILL)
200
	{
201
		syslog(LOG_INFO, "Terminating program! Killed by signal %d", sig);
202
 
203
		if (heat != nullptr)
204
		{
205
			heat->stopRun();
206
			sleep(2);
207
		}
208
 
54 andreas 209
		if (bcmInitialized)
210
			bcm2835_close();
211
 
5 andreas 212
		exit(0);
213
	}
214
}
215
 
216
void changeToUser(const char *usr, const char *grp)
217
{
218
gid_t gr_gid;
219
 
220
	if (usr && strlen(usr))
221
	{
222
		/* get uid */
223
		struct passwd *userpwd;
224
		struct group *usergrp;
225
 
226
		if ((userpwd = getpwnam(usr)) == NULL)
227
		{
228
			syslog(LOG_DAEMON,"no such user: %s", usr);
229
			exit(EXIT_FAILURE);
230
		}
231
 
232
		if (!grp || !strlen(grp) || (usergrp = getgrnam(grp)) == NULL)
233
		{
234
			if (grp && strlen(grp))
235
				syslog(LOG_WARNING,"no such group: %s", grp);
236
 
237
			gr_gid = userpwd->pw_gid;
238
		}
239
		else
240
			gr_gid = usergrp->gr_gid;
241
 
242
		if (setgid(gr_gid) == -1)
243
		{
244
			syslog(LOG_DAEMON,"cannot setgid of user %s: %s", usr, strerror(errno));
245
			//	      exit(EXIT_FAILURE);
246
		}
247
 
248
#ifdef _BSD_SOURCE
249
		/* init suplementary groups
250
		 * (must be done before we change our uid)
251
		 */
252
		if (initgroups(usr, gr_gid) == -1)
253
			syslog(LOG_DAEMON,"Cannot init suplementary groups of user %s: %s", usr, strerror(errno));
254
#endif
255
 
256
		/* set uid */
257
		if (setuid(userpwd->pw_uid) == -1)
258
		{
259
			syslog(LOG_DAEMON,"Cannot change to uid of user %s: %s\n", usr, strerror(errno));
260
			//	      exit(EXIT_FAILURE);
261
		}
262
 
263
		if(userpwd->pw_dir)
264
			setenv("HOME", userpwd->pw_dir, 1);
265
	}
266
}
267
 
268
void *pthr_Heat(void * pV_data)
269
{
270
	pthread_mutex_lock (&fastmutex_proc);
271
 
272
	if (heat != nullptr && !heat->statusRun())
273
		heat->run();
274
 
275
	pthread_mutex_unlock(&fastmutex_proc);
276
}
277
 
278
void *pthr_parser(void *pV_data)
279
{
280
char ch, str[INET_ADDRSTRLEN];
281
// socket structure
282
struct sockaddr_in client1, server1;
283
struct servent *serviceInfo;
284
int s1, s;
285
socklen_t length;
286
 
287
	// socket server
288
	if (Configure.port > 0)
289
		server1.sin_port = htons(Configure.port);
290
	else if ((serviceInfo = getservbyname ("heating", "tcp")))
291
		server1.sin_port = serviceInfo->s_port;
292
	else
293
	{
294
		syslog(LOG_DAEMON,"Error: No TCP port defined!");
295
		exit(EXIT_FAILURE);
296
	}
297
 
298
	server1.sin_family = AF_INET;
299
	server1.sin_addr.s_addr = INADDR_ANY;
300
	s = socket(AF_INET,SOCK_STREAM,0);
301
 
302
	if (s < 0)
303
	{
304
		syslog (LOG_DAEMON, "Error in socket: %s",strerror(errno));
305
		exit(EXIT_FAILURE);
306
	}
307
 
308
	if (bind (s, (struct sockaddr *)&server1, sizeof (server1)) < 0)
309
	{
310
		syslog (LOG_DAEMON, "Error in bind: %s", strerror(errno));
311
		exit(EXIT_FAILURE);
312
	}
313
 
314
	if (listen (s, 5) < 0)
315
	{
316
		syslog (LOG_DAEMON, "Error in listen: %s", strerror(errno));
317
		exit(EXIT_FAILURE);
318
	}
319
 
320
	length = sizeof(client1);
321
 
322
	if (Configure.debug)
323
		syslog (LOG_DEBUG, "Server ready: %d",s);
324
	// Initialize and start the probe
325
	if (heat == nullptr)
326
		heat = new heating();
327
 
328
	if (pthread_create(&pthr_heat, NULL, pthr_Heat, (void *)0) != 0)
329
	{
330
		syslog (LOG_DAEMON,"Create of thread \"pthr_heat\" failed!");
331
		close (s);
332
		exit(EXIT_FAILURE);
333
	}
334
 
335
	if (Configure.debug)
336
		syslog(LOG_DEBUG, "Heating was initialized and is polling ...");
337
 
338
	while (heat->statusRun())
339
	{
340
		if ((s1 = accept (s, (struct sockaddr *)&client1, &length)) < 0)
341
		{
342
			syslog (LOG_DAEMON, "Error in accept: %d: %s", s1, strerror(errno));
343
			continue;
344
		}
345
 
346
		inet_ntop(AF_INET, &(client1.sin_addr), str, INET_ADDRSTRLEN);
347
 
348
		if (Configure.debug)
349
			syslog (LOG_DEBUG, "Connected to client %s", str);
350
 
351
		soc.ip[0] = 0;
352
		soc.sockfd = s;
353
		soc.newfd = s1;
354
 
355
		// This thread will parse the commands comming to this daemon.
356
		if (pthread_create(&pthr_process, NULL, processCommands, (void *)&soc) != 0)
357
		{
358
			syslog (LOG_DAEMON,"Create of thread \"processCommands\" failed!");
359
			close(s1);
360
			soc.newfd = 0;
361
		}
362
	}
363
 
364
	close (s);
365
	soc.sockfd = 0;
366
	delete heat;
367
	heat = nullptr;
368
	return NULL;
369
}
370
 
371
void *processCommands(void *pV_data)
372
{
373
char ch, buf[128];
374
int i, s1, s;
375
bool initialized = false;
376
struct SOCKETS *socket;
377
 
378
	socket = (struct SOCKETS *)pV_data;
379
	s1 = socket->newfd;
380
	s = socket->sockfd;
381
	heat->setHandle(s1);
382
 
383
	if (Configure.debug)
384
		syslog(LOG_DEBUG, "Starting to process commands ...");
385
 
48 andreas 386
	i = 0;
387
	strcpy(buf, "HEATING:READY;");
388
	write(s1, buf, strlen(buf));
5 andreas 389
	memset(&buf[0], 0, sizeof(buf));
48 andreas 390
 
5 andreas 391
	while (heat->statusRun() && read(s1,&ch,1) > 0)
392
	{
393
		if (i < (int)(sizeof(buf) - 1))
394
		{
395
			buf[i] = ch;
396
 
397
			if (ch < 0x0a || ch == 0x0b || ch == 0x0c)
398
				continue;
399
 
400
			if (ch == ';' || ch == 0x0d || ch == 0x0a)
401
			{
402
				int pstat;
403
 
404
				buf[i] = 0;
405
 
406
				if (!strncmp(buf, "quit", 4))
407
					break;
408
 
409
				if ((pstat = parseCommand(s1, buf)) == 0)
410
				{
411
					char hv0[128];
412
 
413
					sprintf(&hv0[0],"INVALID COMMAND:%s;",buf);
414
					write(s1,hv0,strlen(hv0));
415
				}
416
				else if (pstat == 2)
417
					write(s1, "NAK;", 4);
418
				else
419
					write(s1,"OK;",3);
420
 
421
				memset(&buf[0], 0, sizeof(buf));
422
				i = 0;
423
				continue;
424
			}
425
		}
426
 
427
		i++;
428
	}
429
 
430
	heat->removeHandle(s1);
431
	close(s1);
432
	socket->newfd = 0;
433
}
434
 
435
int parseCommand(int s1, char *buf)
436
{
437
std::string line(buf);
438
char hv0[64];
439
std::string vname, vcontent;
440
helper help;
441
 
442
	if (line.find(":") != std::string::npos)
443
	{
444
		vname = line.substr(0, line.find(":"));
445
		vcontent = line.substr(line.find(":") + 1);
446
 
447
		if (Configure.debug)
448
			syslog(LOG_DEBUG, "Command: %s, Content: %s", vname.c_str(), vcontent.c_str());
449
	}
450
	else
451
	{
452
		vname = line;
453
		vcontent = "";
454
 
455
		if (Configure.debug)
456
			syslog(LOG_DEBUG, "Command only: %s", vname.c_str());
457
	}
458
 
459
	// INCREMENT:<room>;
460
	if (vname.compare("INCREMENT") == 0 && vcontent.length() > 0)
461
	{
462
		int room = atoi(vcontent.c_str());
463
		heat->incSoll(room);
464
		return 1;
465
	}
466
 
467
	// DECREMENT:<room>;
468
	if (vname.compare("DECREMENT") == 0 && vcontent.length() > 0)
469
	{
470
		int room = atoi(vcontent.c_str());
471
		heat->decSoll(room);
472
		return 1;
473
	}
474
 
475
	// GET CONFIG;
476
	if (vname.compare("GET CONFIG") == 0)
477
	{
478
		heat->getConfig(s1);
479
		return 1;
480
	}
481
 
482
	// POWER:<ON|OFF>;
483
	if (vname.compare("POWER") == 0)
484
	{
485
		if (vcontent.compare("ON") == 0)
486
			heat->switchOnOff(true);
487
		else
488
			heat->switchOnOff(false);
489
 
490
		return 1;
491
	}
492
 
9 andreas 493
	// SET SOLL:<room>:<soll>; --> Solltemperatur direkt setzen
494
	if (vname.compare("SET SOLL") == 0)
495
	{
496
		std::vector<std::string> s = help.split(vcontent, ':');
497
 
498
		if (s.size() != 2)
499
			return 0;
500
 
501
		int room = atoi(s.at(0).c_str());
502
		double soll = atof(s.at(1).c_str());
503
 
504
		if (!room || soll < 10.0 || soll > 30.0)
505
			return 0;
506
 
507
		heat->setSoll(room, soll);
508
	}
509
 
510
	// SET NIGHT:<room>:<soll>;
511
	if (vname.compare("SET NIGHT") == 0)
512
	{
513
		std::vector<std::string> s = help.split(vcontent, ':');
514
 
515
		if (s.size() != 2)
516
			return 0;
517
 
518
		int room = atoi(s.at(0).c_str());
519
		double soll = atof(s.at(1).c_str());
520
 
521
		if (room < 0 || soll < 10.0 || soll > 30.0)
522
			return 0;
523
 
524
		heat->setNight(room, soll);
525
	}
526
 
527
	// SET MINIMAL:<room>:<soll>;
528
	if (vname.compare("SET MINIMAL") == 0)
529
	{
530
		std::vector<std::string> s = help.split(vcontent, ':');
531
 
532
		if (s.size() != 2)
533
			return 0;
534
 
535
		int room = atoi(s.at(0).c_str());
536
		double soll = atof(s.at(1).c_str());
537
 
538
		if (room < 0 || soll < 10.0 || soll > 30.0)
539
			return 0;
540
 
541
		heat->setMinimal(room, soll);
542
	}
543
 
544
	// NIGHT:<soll>;
545
	if (vname.compare("NIGHT") == 0)
546
	{
547
		double soll = atof(vcontent.c_str());
548
		heat->setNight(0, soll);
549
	}
550
 
551
	// MINIMAL:<soll>;
552
	if (vname.compare("MINIMAL") == 0)
553
	{
554
		double soll = atof(vcontent.c_str());
555
		heat->setMinimal(0, soll);
556
	}
557
 
5 andreas 558
	return 0;
559
}