Subversion Repositories heating

Rev

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