Subversion Repositories general

Rev

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