Subversion Repositories heating

Rev

Rev 54 | 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);
56 andreas 105
	/* Prepare the thread attributes */
106
	if (pthread_attr_init(&pattr) != 0)
107
	{
108
		syslog(LOG_DAEMON,"Error getting thread attributes.");
109
		return 0;
110
	}
111
 
112
	if (pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED) != 0)
113
	{
114
		syslog(LOG_DAEMON,"Error setting thread attributes.");
115
		return 0;
116
	}
117
 
3 andreas 118
	// Now start our Thread
119
	if (pthread_create(&pthr_pars, NULL, pthr_parser, (void *)0) != 0)
120
	{
121
		syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
5 andreas 122
		return 2;
3 andreas 123
	}
56 andreas 124
 
125
	pthread_join(pthr_pars, NULL);			// Wait for just started thread to end
126
 
127
/*	while (1)
3 andreas 128
		sleep(3600);
56 andreas 129
*/
3 andreas 130
	return 0;
131
}
5 andreas 132
 
133
/*
134
 * Detach application from console and make it a daemon.
135
 */
136
void daemon_start (int ignsigcld)
137
{
138
	int childpid, fd;
139
	char hv0[128];
140
 
141
	if (getpid () == 1)
142
		goto out;
143
 
144
	#ifdef SIGTTOU
145
	signal (SIGTTOU, SIG_IGN);
146
	#endif
147
	#ifdef SIGTTIN
148
	signal (SIGTTIN, SIG_IGN);
149
	#endif
150
	#ifdef SIGTSTP
151
	signal (SIGTSTP, SIG_IGN);
152
	#endif
153
 
154
	if ((childpid = fork ()) < 0)
155
		fprintf (stderr, "Can't fork this child\n");
156
	else if (childpid > 0)
157
		exit (0);
158
 
159
	if (setpgrp () == -1)
160
		fprintf (stderr, "Can't change process group\n");
161
 
162
	signal (SIGHUP, SIG_IGN);
163
 
164
	if ((childpid = fork ()) < 0)
165
		syslog (LOG_DAEMON, "Can't fork second child");
166
	else if (childpid > 0)
167
		exit (0);            /* first child */
168
 
169
	/* second child */
170
out:
171
	for (fd = 0; fd < NOFILE; fd++)
172
		close (fd);
173
 
174
	errno = 0;
175
	chdir ("/");
176
	umask (0);
177
 
178
	if (ignsigcld)
179
		signal (SIGCLD, SIG_IGN);
180
 
181
	// Define a signal handler for terminate signals
182
	if (signal(SIGTERM, sig_handler) == SIG_ERR)
183
		syslog(LOG_WARNING,"Can't catch signal SIGTERM!");
184
 
185
	// Create PID file
186
	if ((fd = open(Configure.pidfile, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
187
	{
188
		syslog(LOG_WARNING,"Can't create PID file %s: %s",Configure.pidfile, strerror(errno));
189
		return;
190
	}
191
 
192
	sprintf(hv0, "%d", getpid());
193
	write(fd, hv0, strlen(hv0));
194
	close(fd);
195
}
196
 
197
void sig_child ()
198
{
199
#if defined(BSD) && !defined(sinix) && !defined(Linux)
200
	int pid;
201
	union wait status;
202
 
203
	while ((pid = wait3 (&status, WNOHANG, (struct rusage *)0)) > 0)
204
		;
205
#endif
206
}
207
 
208
/*
209
 * This is the signal handler who disconnects from network and terminates
210
 * this daemon
211
 */
212
void sig_handler(int sig)
213
{
214
	if (sig == SIGTERM || sig == SIGKILL)
215
	{
216
		syslog(LOG_INFO, "Terminating program! Killed by signal %d", sig);
217
 
218
		if (heat != nullptr)
219
		{
220
			heat->stopRun();
221
			sleep(2);
222
		}
223
 
54 andreas 224
		if (bcmInitialized)
225
			bcm2835_close();
226
 
5 andreas 227
		exit(0);
228
	}
229
}
230
 
231
void changeToUser(const char *usr, const char *grp)
232
{
233
gid_t gr_gid;
234
 
235
	if (usr && strlen(usr))
236
	{
237
		/* get uid */
238
		struct passwd *userpwd;
239
		struct group *usergrp;
240
 
241
		if ((userpwd = getpwnam(usr)) == NULL)
242
		{
243
			syslog(LOG_DAEMON,"no such user: %s", usr);
244
			exit(EXIT_FAILURE);
245
		}
246
 
247
		if (!grp || !strlen(grp) || (usergrp = getgrnam(grp)) == NULL)
248
		{
249
			if (grp && strlen(grp))
250
				syslog(LOG_WARNING,"no such group: %s", grp);
251
 
252
			gr_gid = userpwd->pw_gid;
253
		}
254
		else
255
			gr_gid = usergrp->gr_gid;
256
 
257
		if (setgid(gr_gid) == -1)
258
		{
259
			syslog(LOG_DAEMON,"cannot setgid of user %s: %s", usr, strerror(errno));
260
			//	      exit(EXIT_FAILURE);
261
		}
262
 
263
#ifdef _BSD_SOURCE
264
		/* init suplementary groups
265
		 * (must be done before we change our uid)
266
		 */
267
		if (initgroups(usr, gr_gid) == -1)
268
			syslog(LOG_DAEMON,"Cannot init suplementary groups of user %s: %s", usr, strerror(errno));
269
#endif
270
 
271
		/* set uid */
272
		if (setuid(userpwd->pw_uid) == -1)
273
		{
274
			syslog(LOG_DAEMON,"Cannot change to uid of user %s: %s\n", usr, strerror(errno));
275
			//	      exit(EXIT_FAILURE);
276
		}
277
 
278
		if(userpwd->pw_dir)
279
			setenv("HOME", userpwd->pw_dir, 1);
280
	}
281
}
282
 
283
void *pthr_Heat(void * pV_data)
284
{
285
	pthread_mutex_lock (&fastmutex_proc);
286
 
287
	if (heat != nullptr && !heat->statusRun())
288
		heat->run();
289
 
290
	pthread_mutex_unlock(&fastmutex_proc);
291
}
292
 
293
void *pthr_parser(void *pV_data)
294
{
295
char ch, str[INET_ADDRSTRLEN];
296
// socket structure
297
struct sockaddr_in client1, server1;
298
struct servent *serviceInfo;
299
int s1, s;
300
socklen_t length;
301
 
302
	// socket server
303
	if (Configure.port > 0)
304
		server1.sin_port = htons(Configure.port);
305
	else if ((serviceInfo = getservbyname ("heating", "tcp")))
306
		server1.sin_port = serviceInfo->s_port;
307
	else
308
	{
309
		syslog(LOG_DAEMON,"Error: No TCP port defined!");
56 andreas 310
		pthread_exit(NULL);
5 andreas 311
	}
312
 
313
	server1.sin_family = AF_INET;
314
	server1.sin_addr.s_addr = INADDR_ANY;
315
	s = socket(AF_INET,SOCK_STREAM,0);
316
 
317
	if (s < 0)
318
	{
319
		syslog (LOG_DAEMON, "Error in socket: %s",strerror(errno));
56 andreas 320
		pthread_exit(NULL);
5 andreas 321
	}
322
 
323
	if (bind (s, (struct sockaddr *)&server1, sizeof (server1)) < 0)
324
	{
325
		syslog (LOG_DAEMON, "Error in bind: %s", strerror(errno));
56 andreas 326
		pthread_exit(NULL);
5 andreas 327
	}
328
 
329
	if (listen (s, 5) < 0)
330
	{
331
		syslog (LOG_DAEMON, "Error in listen: %s", strerror(errno));
56 andreas 332
		pthread_exit(NULL);
5 andreas 333
	}
334
 
335
	length = sizeof(client1);
336
 
337
	if (Configure.debug)
338
		syslog (LOG_DEBUG, "Server ready: %d",s);
339
	// Initialize and start the probe
340
	if (heat == nullptr)
341
		heat = new heating();
342
 
56 andreas 343
	if (pthread_create(&pthr_heat, &pattr, pthr_Heat, (void *)0) != 0)
5 andreas 344
	{
345
		syslog (LOG_DAEMON,"Create of thread \"pthr_heat\" failed!");
346
		close (s);
56 andreas 347
		pthread_exit(NULL);
5 andreas 348
	}
349
 
350
	if (Configure.debug)
351
		syslog(LOG_DEBUG, "Heating was initialized and is polling ...");
352
 
353
	while (heat->statusRun())
354
	{
355
		if ((s1 = accept (s, (struct sockaddr *)&client1, &length)) < 0)
356
		{
357
			syslog (LOG_DAEMON, "Error in accept: %d: %s", s1, strerror(errno));
358
			continue;
359
		}
360
 
361
		inet_ntop(AF_INET, &(client1.sin_addr), str, INET_ADDRSTRLEN);
362
 
363
		if (Configure.debug)
364
			syslog (LOG_DEBUG, "Connected to client %s", str);
365
 
366
		soc.ip[0] = 0;
367
		soc.sockfd = s;
368
		soc.newfd = s1;
369
 
370
		// This thread will parse the commands comming to this daemon.
56 andreas 371
		if (pthread_create(&pthr_process, &pattr, processCommands, (void *)&soc) != 0)
5 andreas 372
		{
373
			syslog (LOG_DAEMON,"Create of thread \"processCommands\" failed!");
374
			close(s1);
375
			soc.newfd = 0;
376
		}
377
	}
378
 
379
	close (s);
380
	soc.sockfd = 0;
381
	delete heat;
382
	heat = nullptr;
56 andreas 383
	pthread_exit(NULL);
5 andreas 384
}
385
 
386
void *processCommands(void *pV_data)
387
{
388
char ch, buf[128];
389
int i, s1, s;
390
bool initialized = false;
391
struct SOCKETS *socket;
392
 
393
	socket = (struct SOCKETS *)pV_data;
394
	s1 = socket->newfd;
395
	s = socket->sockfd;
396
	heat->setHandle(s1);
397
 
398
	if (Configure.debug)
399
		syslog(LOG_DEBUG, "Starting to process commands ...");
400
 
48 andreas 401
	i = 0;
402
	strcpy(buf, "HEATING:READY;");
403
	write(s1, buf, strlen(buf));
5 andreas 404
	memset(&buf[0], 0, sizeof(buf));
48 andreas 405
 
5 andreas 406
	while (heat->statusRun() && read(s1,&ch,1) > 0)
407
	{
408
		if (i < (int)(sizeof(buf) - 1))
409
		{
410
			buf[i] = ch;
411
 
412
			if (ch < 0x0a || ch == 0x0b || ch == 0x0c)
413
				continue;
414
 
415
			if (ch == ';' || ch == 0x0d || ch == 0x0a)
416
			{
417
				int pstat;
418
 
419
				buf[i] = 0;
420
 
421
				if (!strncmp(buf, "quit", 4))
422
					break;
423
 
424
				if ((pstat = parseCommand(s1, buf)) == 0)
425
				{
426
					char hv0[128];
427
 
428
					sprintf(&hv0[0],"INVALID COMMAND:%s;",buf);
429
					write(s1,hv0,strlen(hv0));
430
				}
431
				else if (pstat == 2)
432
					write(s1, "NAK;", 4);
433
				else
434
					write(s1,"OK;",3);
435
 
436
				memset(&buf[0], 0, sizeof(buf));
437
				i = 0;
438
				continue;
439
			}
440
		}
441
 
442
		i++;
443
	}
444
 
445
	heat->removeHandle(s1);
446
	close(s1);
447
	socket->newfd = 0;
448
}
449
 
450
int parseCommand(int s1, char *buf)
451
{
452
std::string line(buf);
453
char hv0[64];
454
std::string vname, vcontent;
455
helper help;
456
 
457
	if (line.find(":") != std::string::npos)
458
	{
459
		vname = line.substr(0, line.find(":"));
460
		vcontent = line.substr(line.find(":") + 1);
461
 
462
		if (Configure.debug)
463
			syslog(LOG_DEBUG, "Command: %s, Content: %s", vname.c_str(), vcontent.c_str());
464
	}
465
	else
466
	{
467
		vname = line;
468
		vcontent = "";
469
 
470
		if (Configure.debug)
471
			syslog(LOG_DEBUG, "Command only: %s", vname.c_str());
472
	}
473
 
474
	// INCREMENT:<room>;
475
	if (vname.compare("INCREMENT") == 0 && vcontent.length() > 0)
476
	{
477
		int room = atoi(vcontent.c_str());
478
		heat->incSoll(room);
479
		return 1;
480
	}
481
 
482
	// DECREMENT:<room>;
483
	if (vname.compare("DECREMENT") == 0 && vcontent.length() > 0)
484
	{
485
		int room = atoi(vcontent.c_str());
486
		heat->decSoll(room);
487
		return 1;
488
	}
489
 
490
	// GET CONFIG;
491
	if (vname.compare("GET CONFIG") == 0)
492
	{
493
		heat->getConfig(s1);
494
		return 1;
495
	}
496
 
497
	// POWER:<ON|OFF>;
498
	if (vname.compare("POWER") == 0)
499
	{
500
		if (vcontent.compare("ON") == 0)
501
			heat->switchOnOff(true);
502
		else
503
			heat->switchOnOff(false);
504
 
505
		return 1;
506
	}
507
 
9 andreas 508
	// SET SOLL:<room>:<soll>; --> Solltemperatur direkt setzen
509
	if (vname.compare("SET SOLL") == 0)
510
	{
511
		std::vector<std::string> s = help.split(vcontent, ':');
512
 
513
		if (s.size() != 2)
514
			return 0;
515
 
516
		int room = atoi(s.at(0).c_str());
517
		double soll = atof(s.at(1).c_str());
518
 
519
		if (!room || soll < 10.0 || soll > 30.0)
520
			return 0;
521
 
522
		heat->setSoll(room, soll);
523
	}
524
 
525
	// SET NIGHT:<room>:<soll>;
526
	if (vname.compare("SET NIGHT") == 0)
527
	{
528
		std::vector<std::string> s = help.split(vcontent, ':');
529
 
530
		if (s.size() != 2)
531
			return 0;
532
 
533
		int room = atoi(s.at(0).c_str());
534
		double soll = atof(s.at(1).c_str());
535
 
536
		if (room < 0 || soll < 10.0 || soll > 30.0)
537
			return 0;
538
 
539
		heat->setNight(room, soll);
540
	}
541
 
542
	// SET MINIMAL:<room>:<soll>;
543
	if (vname.compare("SET MINIMAL") == 0)
544
	{
545
		std::vector<std::string> s = help.split(vcontent, ':');
546
 
547
		if (s.size() != 2)
548
			return 0;
549
 
550
		int room = atoi(s.at(0).c_str());
551
		double soll = atof(s.at(1).c_str());
552
 
553
		if (room < 0 || soll < 10.0 || soll > 30.0)
554
			return 0;
555
 
556
		heat->setMinimal(room, soll);
557
	}
558
 
559
	// NIGHT:<soll>;
560
	if (vname.compare("NIGHT") == 0)
561
	{
562
		double soll = atof(vcontent.c_str());
563
		heat->setNight(0, soll);
564
	}
565
 
566
	// MINIMAL:<soll>;
567
	if (vname.compare("MINIMAL") == 0)
568
	{
569
		double soll = atof(vcontent.c_str());
570
		heat->setMinimal(0, soll);
571
	}
572
 
5 andreas 573
	return 0;
574
}