Subversion Repositories general

Rev

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

Rev Author Line No. Line
1140 dev 1
/******************************************************************
1159 dev 2
 * rebootd.
1140 dev 3
 *
1145 dev 4
 * A daemon which waits on specified TCP port for special string
1159 dev 5
 * (password) and reboots the computer.
1140 dev 6
 *
1164 dev 7
 * Command line: rebootd [--version] [--help] [--interface IP] [--port PORT] [--config CONFIG_FILE]
1159 dev 8
 * Defaults are port 19 and config /etc/rebootd.conf.
1140 dev 9
 *
10
 * Config file looks like:
11
 * --------------------------
1164 dev 12
 * interface=192.168.0.1
1140 dev 13
 * port=19
14
 * password="some password"
15
 * --------------------------
16
 *
17
 * Usage example (from any other host):
18
 * echo -n "some password" | nc host_to_rebot 19
19
 *
20
 * Copyleft 2005 Anatoli Klassen
21
 *
22
 ******************************************************************/
23
 
24
#include <stdlib.h>
25
#include <stdio.h>
26
#include <string.h>
27
#include <stdarg.h>
1144 dev 28
#include <ctype.h>
1140 dev 29
#include <errno.h>
30
#include <signal.h>
31
#include <unistd.h>
32
#include <netdb.h>
33
#include <syslog.h>
1165 dev 34
#include <signal.h>
35
#include <fcntl.h>
1140 dev 36
#include <sys/types.h>
1144 dev 37
#include <sys/stat.h>
1140 dev 38
#include <sys/socket.h>
39
#include <sys/wait.h>
40
#include <sys/reboot.h>
41
#include <netinet/in.h>
42
#include <arpa/inet.h>
43
 
1145 dev 44
/* name version number */
1159 dev 45
#define APP_NAME          "rebootd"
1145 dev 46
#define VERSION           "1.0"
47
#define REVISION          "$Rev: 1165 $"
1144 dev 48
 
1145 dev 49
#define DEFAULT_PORT      19
1159 dev 50
#define DEFAULT_CFG_FILE  "/etc/rebootd.conf"
1145 dev 51
#define CMDSIZE           4096
52
#define BUFSIZE           4096
53
#define MAX_CONFIG_LINE   4096
54
#define PORT_MIN          1
55
#define PORT_MAX          65535
1140 dev 56
 
1145 dev 57
/* return values of functions */
58
#define RESULT_OK         0
59
#define RESULT_EXIT       1
60
#define RESULT_ERROR      2
61
#define RESULT_UNEXPECTED 3
1140 dev 62
 
1145 dev 63
/* return values for the whole program */
64
#define EXIT_OK           0
65
#define EXIT_USER_ERROR   1
66
#define EXIT_UNEXPECTED   2
67
 
68
/* command line and config file parameters */
69
#define PARAM_HELP_1      "-h"
70
#define PARAM_HELP_2      "--help"
1165 dev 71
#define PARAM_VERSION     "--version"
72
#define PARAM_INTERFACE   "--interface"
73
#define PARAM_PORT        "--port"
74
#define PARAM_CONFIG      "--config"
75
#define PARAM_PID_FILE    "--pid"
1164 dev 76
#define CONFIG_INTERFACE  "interface"
1145 dev 77
#define CONFIG_PORT       "port"
78
#define CONFIG_PASSWORD   "password"
1165 dev 79
#define CONFIG_PID_FILE   "pid"
1145 dev 80
 
1165 dev 81
/* priority of defferent sources of options */
82
#define CFG_PRIO_CMD      1
83
#define CFG_PRIO_CONFIG   2
84
#define CFG_PRIO_DEFAULT  3
85
 
1140 dev 86
struct config {
1164 dev 87
	char           config_file[FILENAME_MAX];
1165 dev 88
	char           config_file_prio;
1164 dev 89
	struct in_addr interface;
1165 dev 90
	char           interface_prio;
1164 dev 91
	ushort         port;
1165 dev 92
	char           port_prio;
1164 dev 93
	char           password[MAX_CONFIG_LINE];
1165 dev 94
	char           password_prio;
95
	char           pid_file[FILENAME_MAX];
96
	char           pid_file_prio;
1140 dev 97
};
1165 dev 98
 
99
static struct config global_cfg;
100
static int           daemonized      = 0; /* we are already a daemon */
101
volatile static int  reconfig_needed = 0; /* we have to stop socket listening and reread config */
1140 dev 102
 
1163 dev 103
static int parse_cmd_line(struct config *cfg, int argc, const char* argv[]);
104
static int parse_config_file(struct config *cfg, const char *config_name);
1156 dev 105
static int get_revision(void);
1165 dev 106
static void check_return(int res);
1140 dev 107
 
1156 dev 108
static uint min(uint a, uint b)
1140 dev 109
{
110
	return (a < b) ? a : b;
111
}
112
 
1164 dev 113
static int establish(const struct config *cfg)
1140 dev 114
{
115
	int s;
116
	struct sockaddr_in sa;
117
 
118
	memset(&sa, 0, sizeof(struct sockaddr_in));
119
 
120
	sa.sin_family      = AF_INET;
1164 dev 121
	sa.sin_port        = htons(cfg->port);
122
	sa.sin_addr.s_addr = (cfg->interface.s_addr ? cfg->interface.s_addr : INADDR_ANY);
1140 dev 123
 
124
	if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
125
		return -1;
126
 
127
	if(bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
128
		close(s);
129
		return -1;
130
	}
131
 
1155 dev 132
	if(listen(s, 1) != 0) {
133
		close(s);
134
		return -1;
135
	}
1140 dev 136
 
137
	return s;
138
}
139
 
1156 dev 140
static void listen_socket(const struct config *cfg, int soc)
1142 dev 141
{
142
	int                t;
143
	char               cmd[CMDSIZE];
144
	char               buf[BUFSIZE];
145
	int                br;
146
	struct sockaddr_in sa;
147
	socklen_t          sa_len;
1155 dev 148
	int                cmdlen;
1142 dev 149
 
150
	for(;;) {
151
		sa_len = sizeof(sa);
1156 dev 152
		if((t = accept(soc, (struct sockaddr *)&sa, &sa_len)) < 0) {
1165 dev 153
			if(errno == EINTR) { /* EINTR might happen on accept(), */
154
				if(reconfig_needed) return;
155
			}
156
			else {
157
				syslog(LOG_ERR, "cannot get connection, %s", strerror(errno));
158
			}
1142 dev 159
			continue;
160
		}
161
 
1155 dev 162
		syslog(LOG_INFO, "connect from %s", inet_ntoa(sa.sin_addr));
1142 dev 163
 
164
		cmd[0] = '\0';
1155 dev 165
		cmdlen = 0;
1142 dev 166
		while((br = recv(t, buf, BUFSIZE, 0)) > 0) {
1156 dev 167
			strncat(cmd, buf, min((uint)br, sizeof(cmd) - cmdlen - 1));
168
			cmdlen += min((uint)br, sizeof(cmd) - cmdlen - 1);
1142 dev 169
		}
170
		sleep(1);
171
		close(t);
172
 
1147 dev 173
		syslog(LOG_INFO, "got command");
1142 dev 174
 
1143 dev 175
		if(0 == strncmp(cmd, cfg->password, sizeof(cmd))) {
1142 dev 176
			syslog(LOG_EMERG, "REBOOT");
177
			sleep(5);
178
			if(reboot(RB_AUTOBOOT) < 0) {
1155 dev 179
				syslog(LOG_ERR, "cannot reboot, %s",  strerror(errno));
1142 dev 180
			}
181
		}
182
	}
183
}
184
 
1156 dev 185
static void print_version(void)
1140 dev 186
{
1145 dev 187
	printf("%s %s.%d\n", APP_NAME, VERSION, get_revision());
188
}
189
 
1156 dev 190
static void pring_usage(int argc, const char* argv[])
1145 dev 191
{
192
	print_version();
1159 dev 193
	printf("\nA daemon which waits on specified TCP port for special");
194
	printf(" string (password) and reboots the computer.\n\n");
1164 dev 195
	printf("Usage: %s [--interface IP] [--port PORT] [--config CONFIG_FILE]\n", argv[0]);
1145 dev 196
	printf("   or: %s --version\n", argv[0]);
197
	printf("   or: %s --help\n\n", argv[0]);
1159 dev 198
	printf("Defaults are port 19 and config /etc/rebootd.conf.\n\n");
1145 dev 199
	printf("Config file looks like:\n--------------------------\n");
1164 dev 200
	printf("interface=192.168.0.1\nport=19\npassword=\"some password\"\n--------------------------\n\n");
1145 dev 201
	printf("Then run from any other host:\necho -n \"some password\" | nc host_to_rebot 19\n");
202
}
203
 
1165 dev 204
static int print_error(const char *msg, const char *value)
205
{
206
	if(daemonized) {
207
		syslog(LOG_ERR, "%s", msg);
208
		if(value) syslog(LOG_ERR, " - %s\n", value);
209
		syslog(LOG_ERR, "\n");
210
	}
211
	else {
212
		fprintf(stderr, "%s", msg);
213
		if(value) fprintf(stderr, " - %s\n", value);
214
		fprintf(stderr, "\n");
215
	}
216
 
217
	return RESULT_ERROR;
218
}
219
 
1156 dev 220
static int print_cmd_error(int argc, const char* argv[], const char *msg, const char *value)
1145 dev 221
{
1140 dev 222
	fprintf(stderr, "%s", msg);
223
	if(value) fprintf(stderr, " - %s\n", value);
224
	fprintf(stderr, "\n");
225
 
1145 dev 226
	pring_usage(argc, argv);
227
 
228
	return RESULT_ERROR;
1140 dev 229
}
230
 
1156 dev 231
static int print_config_error(const char *config_name, int line, const char *msg, const char *value)
1140 dev 232
{
1165 dev 233
	if(daemonized) {
234
		syslog(LOG_ERR, "Error in %s, line %d: %s", config_name, line, msg);
235
		if(value) syslog(LOG_ERR, " - %s", value);
236
		syslog(LOG_ERR, "\n");
237
	}
238
	else {
239
		fprintf(stderr, "Error in %s, line %d: %s", config_name, line, msg);
240
		if(value) fprintf(stderr, " - %s", value);
241
		fprintf(stderr, "\n");
242
	}
1140 dev 243
 
1145 dev 244
	return RESULT_ERROR;
1140 dev 245
}
246
 
1165 dev 247
static int check_abs_path(const char *file_name)
248
{
249
	if(file_name && file_name[0] != '\0' && file_name[0] != '/') {
250
		return print_error("File must have an absolute path", file_name);
251
	}
252
 
253
	return RESULT_OK;
254
}
255
 
1156 dev 256
static int read_config(struct config *cfg, int argc, const char* argv[])
1140 dev 257
{
1165 dev 258
	int           res_cmd_line;
259
	int           res_config_file;
1163 dev 260
	struct config cmd_cfg;
261
	struct config file_cfg;
1140 dev 262
 
1164 dev 263
	memset(cfg, 0, sizeof(struct config));
1163 dev 264
	res_cmd_line = parse_cmd_line(&cmd_cfg, argc, argv);
1145 dev 265
	if(res_cmd_line != RESULT_OK) return res_cmd_line;
1165 dev 266
	if(check_abs_path(cmd_cfg.config_file) != RESULT_OK) return RESULT_ERROR;
1140 dev 267
 
1163 dev 268
	res_config_file = parse_config_file(&file_cfg,
269
		cmd_cfg.config_file[0] != '\0' ? cmd_cfg.config_file : DEFAULT_CFG_FILE);
1145 dev 270
	if(res_config_file != RESULT_OK) return res_config_file;
1140 dev 271
 
1165 dev 272
	if(file_cfg.password[0] == '\0')
273
		return print_error("Password is not set", NULL);
274
 
275
	/* save parsed values to general config */
276
	if(cmd_cfg.config_file[0] != '\0') {
277
		strncpy(cfg->config_file, cmd_cfg.config_file, sizeof(cfg->config_file));
278
		cfg->config_file_prio = CFG_PRIO_CMD;
1140 dev 279
	}
1165 dev 280
	else {
281
		strncpy(cfg->config_file, DEFAULT_CFG_FILE, sizeof(cfg->config_file));
282
		cfg->config_file_prio = CFG_PRIO_DEFAULT;
283
	}
1140 dev 284
 
1165 dev 285
	if(cmd_cfg.interface.s_addr) {
286
		cfg->interface      = cmd_cfg.interface;
287
		cfg->interface_prio = CFG_PRIO_CMD;
288
	}
289
	else if(file_cfg.interface.s_addr) {
290
		cfg->interface      = file_cfg.interface;
291
		cfg->interface_prio = CFG_PRIO_CONFIG;
292
	}
293
	else {
294
		cfg->interface_prio = CFG_PRIO_DEFAULT;
295
	}
296
 
297
	if(cmd_cfg.port) {
298
		cfg->port      = cmd_cfg.port;
299
		cfg->port_prio = CFG_PRIO_CMD;
300
	}
301
	else if(file_cfg.port) {
302
		cfg->port      = file_cfg.port;
303
		cfg->port_prio = CFG_PRIO_CONFIG;
304
	}
305
	else {
306
		cfg->port      = DEFAULT_PORT;
307
		cfg->port_prio = CFG_PRIO_DEFAULT;
308
	}
309
 
310
	if(cmd_cfg.pid_file[0] != '\0') {
311
		strncpy(cfg->pid_file, cmd_cfg.pid_file, sizeof(cfg->pid_file));
312
		cfg->pid_file_prio = CFG_PRIO_CMD;
313
	}
314
	else if(file_cfg.pid_file[0] != '\0') {
315
		strncpy(cfg->pid_file, file_cfg.pid_file, sizeof(cfg->pid_file));
316
		cfg->pid_file_prio = CFG_PRIO_CONFIG;
317
	}
318
	else {
319
		cfg->pid_file_prio = CFG_PRIO_DEFAULT;
320
	}
321
	if(check_abs_path(cfg->config_file) != RESULT_OK) return RESULT_ERROR;
322
 
323
	strncpy(cfg->password, file_cfg.password, sizeof(cfg->password));
324
	cfg->password_prio = CFG_PRIO_CONFIG;
325
 
326
	return RESULT_OK;
327
}
328
 
329
static int reread_config(struct config *cfg, int *reinit_needed)
330
{
331
	int           res_config_file;
332
	struct config file_cfg;
333
 
334
	*reinit_needed = 0;
335
 
336
	res_config_file = parse_config_file(&file_cfg, cfg->config_file);
337
	if(res_config_file != RESULT_OK) return res_config_file;
338
 
339
	if(file_cfg.password[0] == '\0')
340
		return print_error("Password is not set", NULL);
341
 
1163 dev 342
	/* save parsed values to general config */
1165 dev 343
	if(cfg->interface_prio >= CFG_PRIO_CONFIG && cfg->interface.s_addr != file_cfg.interface.s_addr)
344
	{
345
		*reinit_needed      = 1;
346
		cfg->interface      = file_cfg.interface;
347
		cfg->interface_prio = CFG_PRIO_CONFIG;
348
	}
1163 dev 349
 
1165 dev 350
	if(cfg->port_prio >= CFG_PRIO_CONFIG && cfg->port != file_cfg.port && file_cfg.port) {
351
		*reinit_needed = 1;
352
		cfg->port      = file_cfg.port;
353
		cfg->port_prio = CFG_PRIO_CONFIG;
354
	}
1164 dev 355
 
1165 dev 356
	if(check_abs_path(cfg->config_file) != RESULT_OK) return RESULT_ERROR;
1140 dev 357
 
1163 dev 358
	strncpy(cfg->password, file_cfg.password, sizeof(cfg->password));
1165 dev 359
	cfg->password_prio = CFG_PRIO_CONFIG;
1163 dev 360
 
1145 dev 361
	return RESULT_OK;
1140 dev 362
}
363
 
1164 dev 364
static int parse_interface(const char *s, char **end, struct in_addr *ip)
365
{
366
	char       buf[MAX_CONFIG_LINE];
367
	int        count;
368
	const char *c;
369
 
370
	c     = s;
371
	count = 0;
372
	while(c[0] != '\0' && !isspace(c[0])) c++, count++;
373
	if(count > MAX_CONFIG_LINE) return RESULT_ERROR;
374
	strncpy(buf, s, count);
375
	buf[count] = '\0';
376
	c++;
377
 
378
	if(end) *end = (char *)c;
379
 
380
	if(inet_aton(buf, ip) == 1)
381
		return RESULT_OK;
382
	else
383
		return RESULT_ERROR;
384
}
385
 
1163 dev 386
static int parse_cmd_line(struct config *cfg, int argc, const char* argv[])
1140 dev 387
{
388
	char *end;
389
	int  i;
1163 dev 390
	long port;
1140 dev 391
 
1163 dev 392
	memset(cfg, 0, sizeof(struct config));
1140 dev 393
 
394
	for(i = 1; i < argc; i++) {
1165 dev 395
		if(0 == strcmp(argv[i], PARAM_INTERFACE)) {
1164 dev 396
			if(cfg->interface.s_addr)
397
				return print_cmd_error(argc, argv, "Interface is already set", NULL);
398
 
399
			if(++i < argc) {
400
				if(parse_interface(argv[i], (char **)NULL, &cfg->interface) != RESULT_OK)
401
					return print_cmd_error(argc, argv, 
402
						"Cannot parse interface", argv[i]);
403
			}
404
			else {
405
				return print_cmd_error(argc, argv, "Interface expected", NULL);
406
			}
407
		}
1165 dev 408
		else if(0 == strcmp(argv[i], PARAM_PORT)) {
1164 dev 409
			if(cfg->port)
1145 dev 410
				return print_cmd_error(argc, argv, "Port is already set", NULL);
1140 dev 411
 
412
			if(++i < argc) {
1163 dev 413
				port = strtol(argv[i], &end, 10);
414
				if(*end != '\0' || port < PORT_MIN || port > PORT_MAX)
1145 dev 415
					return print_cmd_error(argc, argv, 
416
						"Port number must be integer between 1 and 65535", NULL);
1163 dev 417
				cfg->port = (ushort)port;
1140 dev 418
			}
419
			else {
1145 dev 420
				return print_cmd_error(argc, argv, "Port number expected", NULL);
1140 dev 421
			}
422
		}
1165 dev 423
		else if(0 == strcmp(argv[i], PARAM_CONFIG)) {
1163 dev 424
			if(cfg->config_file[0] != '\0')
1165 dev 425
				return print_cmd_error(argc, argv, "Config file name is already set", NULL);
1140 dev 426
 
427
			if(++i < argc) {
1165 dev 428
				strncpy(cfg->config_file, argv[i], FILENAME_MAX);
429
				if(cfg->config_file[FILENAME_MAX - 1] != '\0')
1155 dev 430
					return print_cmd_error(argc, argv,
431
						"Config file name is too long", NULL);
1140 dev 432
			}
433
			else {
1165 dev 434
				return print_cmd_error(argc, argv, "Config file name expected", NULL);
1140 dev 435
			}
436
		}
1165 dev 437
		else if(0 == strcmp(argv[i], PARAM_PID_FILE)) {
438
			if(cfg->pid_file[0] != '\0')
439
				return print_cmd_error(argc, argv, "PID file name is already set", NULL);
440
 
441
			if(++i < argc) {
442
				strncpy(cfg->pid_file, argv[i], FILENAME_MAX);
443
				if(cfg->pid_file[FILENAME_MAX - 1] != '\0')
444
					return print_cmd_error(argc, argv,
445
						"PID file name is too long", NULL);
446
			}
447
			else {
448
				return print_cmd_error(argc, argv, "PID file name expected", NULL);
449
			}
450
		}
451
		else if(0 == strcmp(argv[i], PARAM_VERSION)) {
1145 dev 452
			print_version();
453
			return RESULT_EXIT;
454
		}
455
		else if(0 == strcmp(argv[i], PARAM_HELP_1) || 0 == strcmp(argv[i], PARAM_HELP_2)) {
456
			pring_usage(argc, argv);
457
			return RESULT_EXIT;
458
		}
1140 dev 459
		else {
1145 dev 460
			return print_cmd_error(argc, argv, "Unknown parameter", argv[i]);
1140 dev 461
		}
462
	}
463
 
1145 dev 464
	return RESULT_OK;
1140 dev 465
}
466
 
1164 dev 467
static int validate_equal_sign(const char *config_name, const int count, const char *line,
468
	uint name_len, char **subline)
469
{
470
	const char *c;
1140 dev 471
 
1164 dev 472
	c = line + name_len - 1;
473
	if('=' != c[0] && !isspace(c[0]))
474
		return print_config_error(config_name, count, "Unknown config parameter", line);
475
	while(c[0] != '\0' && isspace(c[0])) c++;
476
	if('=' != c[0])
477
		return print_config_error(config_name, count, "Equal sign expected", NULL);
478
	c++;
479
 
480
	if(subline) *subline = (char *)c;
481
 
482
	return RESULT_OK;
483
}
484
 
485
static int validate_eol(const char *config_name, const int count, const char *line)
486
{
487
	while(line[0] != '\0' && isspace(line[0])) line++;
488
	if('\0' != line[0] && '#' != line[0])
489
		return print_config_error(config_name, count, "End of line expected", NULL);
490
 
491
	return RESULT_OK;
492
}
493
 
1165 dev 494
static int extract_string_value(const char *config_name, const int count, char **subline, int must_quot,
495
	char *string, uint len)
1164 dev 496
{
497
	uint cur_len;
1165 dev 498
	int  quot;
1164 dev 499
 
500
	while(*subline[0] != '\0' && isspace(*subline[0])) (*subline)++;
1165 dev 501
	if('"' == *subline[0]) {
502
		quot = 1;
503
		(*subline)++;
504
	}
505
	else {
506
		if(must_quot) {
507
			return print_config_error(config_name, count, "Open quot expected", NULL);
508
		}
509
		else {
510
			quot = 0;
511
			/* skip spaces if not quoted */
512
			while(*subline[0] != '\0' && isspace(*subline[0])) (*subline)++;
513
		}
514
	}
1164 dev 515
 
516
	cur_len = 0;
1165 dev 517
	while(*subline[0] != '\0')
1164 dev 518
	{
1165 dev 519
		if(cur_len >= len) return print_config_error(config_name, count, "Value too long", NULL);
520
		if(quot && *subline[0] == '"') break;
521
		if(!quot && isspace(*subline[0])) break;
522
 
1164 dev 523
		string[0] = *subline[0];
524
		string++;
525
		(*subline)++;
526
		cur_len++;
527
	}
528
 
529
	string[0] = '\0';
1165 dev 530
	if(quot) {
531
		if('"' != *subline[0])
532
			return print_config_error(config_name, count, "Close quot expected", NULL);
533
		(*subline)++;
534
	}
1164 dev 535
 
536
	return RESULT_OK;
537
}
538
 
1163 dev 539
static int parse_config_file(struct config *cfg, const char *config_name)
1140 dev 540
{
541
	FILE *config_file;
542
	char buf[MAX_CONFIG_LINE];
543
	char *line;
544
	char *subline;
545
	int  count;
1156 dev 546
	uint len;
1163 dev 547
	long port;
1164 dev 548
	int  res;
1140 dev 549
 
1163 dev 550
	memset(cfg, 0, sizeof(struct config));
551
 
1140 dev 552
	config_file = fopen(config_name, "r");
1165 dev 553
	if(!config_file)
554
		return print_error("Can not open config file", strerror(errno));
1140 dev 555
 
556
	count = 0;
557
	while(fgets(buf, sizeof(buf), config_file)) {
558
		count++;
559
		line = buf;
560
 
561
		/* skip end spaces */
1156 dev 562
		len = strlen(line);
1146 dev 563
		if(len == MAX_CONFIG_LINE-1)
564
			return print_config_error(config_name, count, "Line is too long", NULL);
1140 dev 565
		while(len && isspace(line[len-1])) --len;
566
		if(!len) continue;
567
		line[len] = '\0';
568
 
569
		/* skip begin spaces */
570
		while(line[0] != '\0' && isspace(line[0])) line++;
571
 
572
		if('#' == line[0]) { /* skip comment lines */
573
			continue;
574
		}
1164 dev 575
		else if(strncmp(line, CONFIG_INTERFACE, min(sizeof(CONFIG_INTERFACE) - 1, len)) == 0) {
576
			if((res = validate_equal_sign(config_name, count, line,
577
				sizeof(CONFIG_INTERFACE), &subline)) != RESULT_OK) return res;
578
 
579
			if(parse_interface(subline, &subline, &cfg->interface) != RESULT_OK)
580
				return print_config_error(config_name, count,
581
					"Cannot parse interface", NULL);
582
 
583
			if((res = validate_eol(config_name, count, subline)) != RESULT_OK) return res;
584
		}
1140 dev 585
		else if(strncmp(line, CONFIG_PORT, min(sizeof(CONFIG_PORT) - 1, len)) == 0) {
1164 dev 586
			if((res = validate_equal_sign(config_name, count, line, sizeof(CONFIG_PORT),
587
				&subline)) != RESULT_OK) return res;
588
 
1163 dev 589
			port = strtol(subline, &subline, 10);
590
			if(port < PORT_MIN || port > PORT_MAX)
1140 dev 591
				return print_config_error(config_name, count, 
592
					"Port number must be integer between 1 and 65535", NULL);
1164 dev 593
 
594
			if((res = validate_eol(config_name, count, subline)) != RESULT_OK) return res;
1163 dev 595
			cfg->port = (ushort)port;
1140 dev 596
		}
597
		else if(strncmp(line, CONFIG_PASSWORD, min(sizeof(CONFIG_PASSWORD) - 1, len)) == 0) {
1164 dev 598
			if((res = validate_equal_sign(config_name, count, line,
599
				sizeof(CONFIG_PASSWORD), &subline)) != RESULT_OK) return res;
1140 dev 600
 
1165 dev 601
			if((res = extract_string_value(config_name, count, &subline, 1,
1164 dev 602
				cfg->password, sizeof(cfg->password))) != RESULT_OK) return res;
1140 dev 603
 
1164 dev 604
			if((res = validate_eol(config_name, count, subline)) != RESULT_OK) return res;
1140 dev 605
		}
1165 dev 606
		else if(strncmp(line, CONFIG_PID_FILE, min(sizeof(CONFIG_PID_FILE) - 1, len)) == 0) {
607
			if((res = validate_equal_sign(config_name, count, line,
608
				sizeof(CONFIG_PID_FILE), &subline)) != RESULT_OK) return res;
609
 
610
			if((res = extract_string_value(config_name, count, &subline, 0,
611
				cfg->pid_file, sizeof(cfg->pid_file))) != RESULT_OK) return res;
612
 
613
			if((res = validate_eol(config_name, count, subline)) != RESULT_OK) return res;
614
		}
1140 dev 615
		else {
616
			return print_config_error(config_name, count, "Unknown config parameter", line);
617
		}
618
	}
1164 dev 619
	if(ferror(config_file)) {
1165 dev 620
		print_error("Config file reading failed", strerror(errno));
1164 dev 621
	}
1140 dev 622
 
1165 dev 623
	if(fclose(config_file) != 0)
624
		return print_error("Can not close config file", strerror(errno));
1140 dev 625
 
1145 dev 626
	return RESULT_OK;
1140 dev 627
}
628
 
1156 dev 629
static int get_revision(void)
1145 dev 630
{
1156 dev 631
	int        r;
632
	const char *begin;
1145 dev 633
 
634
	begin = REVISION;
635
	while(begin[0] != '\0' && begin[0] != ':') begin++;
636
	if(begin[0] == '\0') return 0;
637
	begin++;
1156 dev 638
	r = (int)strtol(begin, (char**)NULL, 10);
1145 dev 639
 
640
	return r;
641
}
642
 
1165 dev 643
static int save_pid(const char *pid_file, const pid_t pid)
644
{
645
	int  fd;
646
	FILE *file;
647
 
648
	if(!pid_file || pid_file[0] == '\0') return RESULT_OK;
649
 
650
	unlink(pid_file);
651
 
652
	fd = open(pid_file, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
653
	if(fd < 0) {
654
		syslog(LOG_ERR, "Can open PID file %s for write: %s\n", pid_file, strerror(errno));
655
		return RESULT_ERROR;
656
	}
657
 
658
	file = fdopen(fd, "w");
659
	if(!file) {
660
		syslog(LOG_ERR, "Can open PID file %s for write: %s\n", pid_file, strerror(errno));
661
		return RESULT_ERROR;
662
	}
663
 
664
	if(fprintf(file, "%d\n", pid) < 0) {
665
		syslog(LOG_ERR, "Can write PID to file %s: %s\n", pid_file, strerror(errno));
666
		return RESULT_ERROR;
667
	}
668
 
669
	if(fclose(file) != 0) {
670
		syslog(LOG_ERR, "Can not close PID file %s: %s\n", pid_file, strerror(errno));
671
		return RESULT_UNEXPECTED;
672
	}
673
 
674
	return RESULT_OK;
675
}
676
 
1156 dev 677
static int create_child(int soc)
1143 dev 678
{
679
	pid_t child;
680
 
681
	child = fork();
682
	if(child == -1) {
683
		fprintf(stderr, "Cannot fork: %s\n", strerror(errno));
1145 dev 684
		return RESULT_UNEXPECTED; /* an unexpected error */
1143 dev 685
	}
686
	else if(child > 0) {
1156 dev 687
		if(close(soc) != 0) {
1143 dev 688
			fprintf(stderr, "Cannot close socket: %s\n", strerror(errno));
1145 dev 689
			return RESULT_UNEXPECTED; /* an unexpected error */
1143 dev 690
		}
1165 dev 691
 
1145 dev 692
		return RESULT_EXIT; /* we are the parent */
1143 dev 693
	}
694
	else {
1145 dev 695
		return RESULT_OK; /* we are the child */
1143 dev 696
	}
697
}
698
 
699
static int close_descr(int d)
700
{
701
	if(close(d) != 0) {
702
		syslog(LOG_ERR, "Cannot close descriptor %d: %s\n", d, strerror(errno));
1145 dev 703
		return RESULT_UNEXPECTED;
1143 dev 704
	}
1145 dev 705
	return RESULT_OK;
1143 dev 706
}
707
 
1165 dev 708
static void reconfig_signal(void)
709
{
710
	syslog(LOG_INFO, "reconfig");
711
	reconfig_needed = 1;
712
}
713
 
714
static void delete_pid_file(void)
715
{
716
	if(global_cfg.pid_file && global_cfg.pid_file[0] != '\0') {
717
		if(unlink(global_cfg.pid_file) != 0) {
718
			syslog(LOG_ERR, "Cannot delete PID file %s: %s\n",
719
				global_cfg.pid_file, strerror(errno));
720
		}
721
	}
722
}
723
 
724
static void quit_signal(void)
725
{
726
	delete_pid_file();
727
	syslog(LOG_WARNING, "quit");
728
	exit(0);
729
}
730
 
731
static void signal_handler(int sig)
732
{
733
	int saved_errno = errno;
734
 
735
	switch(sig) {
736
		case SIGHUP:
737
			reconfig_signal();
738
			break;
739
 
740
		case SIGINT:
741
		case SIGTERM:
742
		case SIGQUIT:
743
			quit_signal();
744
			break;
745
	}
746
 
747
	errno = saved_errno;
748
}
749
 
750
static int init_signal_handler(int sig)
751
{
752
	struct sigaction sa;
753
 
754
	sa.sa_flags   = 0;
755
	sa.sa_handler = signal_handler;
756
	sigemptyset(&sa.sa_mask);
757
 
758
	if(sigaction(sig, &sa, (struct sigaction *)NULL) != 0) {
759
		syslog(LOG_ERR, "Cannot register handler for signal %d: %s\n", sig, strerror(errno));
760
		return RESULT_UNEXPECTED;
761
	}
762
 
763
	return RESULT_OK;
764
}
765
 
766
static int init_signals(void)
767
{
768
	if(init_signal_handler(SIGHUP)  != RESULT_OK) return RESULT_UNEXPECTED;
769
	if(init_signal_handler(SIGINT)  != RESULT_OK) return RESULT_UNEXPECTED;
770
	if(init_signal_handler(SIGTERM) != RESULT_OK) return RESULT_UNEXPECTED;
771
	if(init_signal_handler(SIGQUIT) != RESULT_OK) return RESULT_UNEXPECTED;
772
 
773
	return RESULT_OK;
774
}
775
 
776
static void check_return(int res)
777
{
778
	if(daemonized && res != RESULT_OK) {
779
		delete_pid_file();
780
		syslog(LOG_WARNING, "quit");
781
	}
782
 
783
	switch(res) {
784
		case RESULT_EXIT:       exit(EXIT_OK);         return;  /* no error but exit */
785
		case RESULT_ERROR:      exit(EXIT_USER_ERROR); return;  /* user error */
786
		case RESULT_UNEXPECTED: exit(EXIT_UNEXPECTED); return;  /* unexpected error */
787
	}
788
}
789
 
1156 dev 790
int main(int argc, const char* argv[])
1140 dev 791
{
1165 dev 792
	int soc;
793
	int reinit_needed;
1140 dev 794
 
1143 dev 795
	/* get config */
1165 dev 796
	check_return(read_config(&global_cfg, argc, argv));
1143 dev 797
 
798
	/* try to listen the port */
1165 dev 799
	if((soc = establish(&global_cfg)) < 0) {
800
		fprintf(stderr, "Cannot listen to port %i\n", global_cfg.port);
1145 dev 801
		return EXIT_USER_ERROR;
1140 dev 802
	}
803
 
1143 dev 804
	/* fork and release the console */
1165 dev 805
	check_return(create_child(soc));
1140 dev 806
 
1143 dev 807
	/* continue as first child */
808
	if(setsid() == -1) {
809
		fprintf(stderr, "Cannot create session: %s\n", strerror(errno));
1145 dev 810
		return EXIT_UNEXPECTED;
1143 dev 811
	}
812
 
813
	/* fork the second time */
1165 dev 814
	check_return(create_child(soc));
815
 
816
	/* continue as final child */
817
	if(init_signals() != RESULT_OK) return EXIT_UNEXPECTED;
818
 
819
	if(chdir("/") < 0) {
820
		fprintf(stderr, "Cannot chdir to /: %s\n", strerror(errno));
821
		return EXIT_UNEXPECTED;
1143 dev 822
	}
1165 dev 823
	umask(0);
824
	check_return(save_pid(global_cfg.pid_file, getpid()));
825
	daemonized = 1;
1143 dev 826
 
1165 dev 827
	/* from now do not use console for error output */
1145 dev 828
	if(close_descr(0) != RESULT_OK) return EXIT_UNEXPECTED;
829
	if(close_descr(1) != RESULT_OK) return EXIT_UNEXPECTED;
830
	if(close_descr(2) != RESULT_OK) return EXIT_UNEXPECTED;
1143 dev 831
 
1165 dev 832
	for(;;) {
833
		syslog(LOG_INFO, "listen on %d", global_cfg.port);
834
		listen_socket(&global_cfg, soc);
1143 dev 835
 
1165 dev 836
		if(reconfig_needed) {
837
			reconfig_needed = 0;
838
			check_return(reread_config(&global_cfg, &reinit_needed));
839
			if(reinit_needed) {
840
				close(soc);
841
 
842
				if((soc = establish(&global_cfg)) < 0) {
843
					syslog(LOG_ERR, "Cannot listen to port %i\n", global_cfg.port);
844
					check_return(RESULT_ERROR);
845
				}
846
			}
847
		}
848
	}
849
 
850
	return EXIT_OK; /* unreacheable */
1140 dev 851
}
852