Subversion Repositories general

Rev

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