Subversion Repositories heating

Rev

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