Subversion Repositories heating

Rev

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