Subversion Repositories general

Rev

Rev 1159 | 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
 *
1159 dev 7
 * Command line: rebootd [--port PORT] [--config CONFIG_FILE]
8
 * Defaults are port 19 and config /etc/rebootd.conf.
1140 dev 9
 *
10
 * Config file looks like:
11
 * --------------------------
12
 * port=19
13
 * password="some password"
14
 * --------------------------
15
 *
16
 * Usage example (from any other host):
17
 * echo -n "some password" | nc host_to_rebot 19
18
 *
19
 * Copyleft 2005 Anatoli Klassen
20
 *
21
 ******************************************************************/
22
 
23
#include <stdlib.h>
24
#include <stdio.h>
25
#include <string.h>
26
#include <stdarg.h>
1144 dev 27
#include <ctype.h>
1140 dev 28
#include <errno.h>
29
#include <signal.h>
30
#include <unistd.h>
31
#include <netdb.h>
32
#include <syslog.h>
33
#include <sys/types.h>
1144 dev 34
#include <sys/stat.h>
1140 dev 35
#include <sys/socket.h>
36
#include <sys/wait.h>
37
#include <sys/reboot.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
40
 
1145 dev 41
/* name version number */
1159 dev 42
#define APP_NAME          "rebootd"
1145 dev 43
#define VERSION           "1.0"
44
#define REVISION          "$Rev: 1163 $"
1144 dev 45
 
1145 dev 46
#define DEFAULT_PORT      19
1159 dev 47
#define DEFAULT_CFG_FILE  "/etc/rebootd.conf"
1145 dev 48
#define CMDSIZE           4096
49
#define BUFSIZE           4096
50
#define MAX_CONFIG_LINE   4096
51
#define PORT_MIN          1
52
#define PORT_MAX          65535
1140 dev 53
 
1145 dev 54
/* return values of functions */
55
#define RESULT_OK         0
56
#define RESULT_EXIT       1
57
#define RESULT_ERROR      2
58
#define RESULT_UNEXPECTED 3
1140 dev 59
 
1145 dev 60
/* return values for the whole program */
61
#define EXIT_OK           0
62
#define EXIT_USER_ERROR   1
63
#define EXIT_UNEXPECTED   2
64
 
65
/* command line and config file parameters */
66
#define PARAM_HELP_1      "-h"
67
#define PARAM_HELP_2      "--help"
68
#define PARAM_VERSION_1   "-v"
69
#define PARAM_VERSION_2   "--version"
70
#define PARAM_PORT_1      "-p"
71
#define PARAM_PORT_2      "--port"
72
#define PARAM_CONFIG_1    "-c"
73
#define PARAM_CONFIG_2    "--config"
74
#define CONFIG_PORT       "port"
75
#define CONFIG_PASSWORD   "password"
76
 
1140 dev 77
struct config {
1163 dev 78
	char   config_file[FILENAME_MAX];
1156 dev 79
	ushort port;
80
	char   password[MAX_CONFIG_LINE];
1140 dev 81
};
82
 
1163 dev 83
static int parse_cmd_line(struct config *cfg, int argc, const char* argv[]);
84
static int parse_config_file(struct config *cfg, const char *config_name);
1156 dev 85
static int get_revision(void);
1140 dev 86
 
1156 dev 87
static uint min(uint a, uint b)
1140 dev 88
{
89
	return (a < b) ? a : b;
90
}
91
 
1156 dev 92
static int establish(ushort port)
1140 dev 93
{
94
	int s;
95
	struct sockaddr_in sa;
96
 
97
	memset(&sa, 0, sizeof(struct sockaddr_in));
98
 
99
	sa.sin_family      = AF_INET;
1156 dev 100
	sa.sin_port        = htons(port);
1140 dev 101
	sa.sin_addr.s_addr = INADDR_ANY;
102
 
103
	if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
104
		return -1;
105
 
106
	if(bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
107
		close(s);
108
		return -1;
109
	}
110
 
1155 dev 111
	if(listen(s, 1) != 0) {
112
		close(s);
113
		return -1;
114
	}
1140 dev 115
 
116
	return s;
117
}
118
 
1156 dev 119
static void listen_socket(const struct config *cfg, int soc)
1142 dev 120
{
121
	int                t;
122
	char               cmd[CMDSIZE];
123
	char               buf[BUFSIZE];
124
	int                br;
125
	struct sockaddr_in sa;
126
	socklen_t          sa_len;
1155 dev 127
	int                cmdlen;
1142 dev 128
 
129
	for(;;) {
130
		sa_len = sizeof(sa);
1156 dev 131
		if((t = accept(soc, (struct sockaddr *)&sa, &sa_len)) < 0) {
1142 dev 132
			if(errno == EINTR) /* EINTR might happen on accept(), */
133
				continue;        /* try again */
134
 
1155 dev 135
			syslog(LOG_ERR, "cannot get connection, %s", strerror(errno));
1142 dev 136
			continue;
137
		}
138
 
1155 dev 139
		syslog(LOG_INFO, "connect from %s", inet_ntoa(sa.sin_addr));
1142 dev 140
 
141
		cmd[0] = '\0';
1155 dev 142
		cmdlen = 0;
1142 dev 143
		while((br = recv(t, buf, BUFSIZE, 0)) > 0) {
1156 dev 144
			strncat(cmd, buf, min((uint)br, sizeof(cmd) - cmdlen - 1));
145
			cmdlen += min((uint)br, sizeof(cmd) - cmdlen - 1);
1142 dev 146
		}
147
		sleep(1);
148
		close(t);
149
 
1147 dev 150
		syslog(LOG_INFO, "got command");
1142 dev 151
 
1143 dev 152
		if(0 == strncmp(cmd, cfg->password, sizeof(cmd))) {
1142 dev 153
			syslog(LOG_EMERG, "REBOOT");
154
			sleep(5);
155
			if(reboot(RB_AUTOBOOT) < 0) {
1155 dev 156
				syslog(LOG_ERR, "cannot reboot, %s",  strerror(errno));
1142 dev 157
			}
158
		}
159
	}
160
}
161
 
1156 dev 162
static void print_version(void)
1140 dev 163
{
1145 dev 164
	printf("%s %s.%d\n", APP_NAME, VERSION, get_revision());
165
}
166
 
1156 dev 167
static void pring_usage(int argc, const char* argv[])
1145 dev 168
{
169
	print_version();
1159 dev 170
	printf("\nA daemon which waits on specified TCP port for special");
171
	printf(" string (password) and reboots the computer.\n\n");
1145 dev 172
	printf("Usage: %s [--port PORT] [--config CONFIG_FILE]\n", argv[0]);
173
	printf("   or: %s --version\n", argv[0]);
174
	printf("   or: %s --help\n\n", argv[0]);
1159 dev 175
	printf("Defaults are port 19 and config /etc/rebootd.conf.\n\n");
1145 dev 176
	printf("Config file looks like:\n--------------------------\n");
177
	printf("port=19\npassword=\"some password\"\n--------------------------\n\n");
178
	printf("Then run from any other host:\necho -n \"some password\" | nc host_to_rebot 19\n");
179
}
180
 
1156 dev 181
static int print_cmd_error(int argc, const char* argv[], const char *msg, const char *value)
1145 dev 182
{
1140 dev 183
	fprintf(stderr, "%s", msg);
184
	if(value) fprintf(stderr, " - %s\n", value);
185
	fprintf(stderr, "\n");
186
 
1145 dev 187
	pring_usage(argc, argv);
188
 
189
	return RESULT_ERROR;
1140 dev 190
}
191
 
1156 dev 192
static int print_config_error(const char *config_name, int line, const char *msg, const char *value)
1140 dev 193
{
194
	fprintf(stderr, "Error in %s, line %d: %s", config_name, line, msg);
195
	if(value) fprintf(stderr, " - %s", value);
196
	fprintf(stderr, "\n");
197
 
1145 dev 198
	return RESULT_ERROR;
1140 dev 199
}
200
 
1145 dev 201
/* return: 0 - no error, continue; 1 - no error, but exit; 2 - user error, exit; 3 - unexpected error, exit */
1156 dev 202
static int read_config(struct config *cfg, int argc, const char* argv[])
1140 dev 203
{
1163 dev 204
	int    res_cmd_line;
205
	int    res_config_file;
206
	struct config cmd_cfg;
207
	struct config file_cfg;
1140 dev 208
 
1163 dev 209
	res_cmd_line = parse_cmd_line(&cmd_cfg, argc, argv);
1145 dev 210
	if(res_cmd_line != RESULT_OK) return res_cmd_line;
1140 dev 211
 
1163 dev 212
	res_config_file = parse_config_file(&file_cfg,
213
		cmd_cfg.config_file[0] != '\0' ? cmd_cfg.config_file : DEFAULT_CFG_FILE);
1145 dev 214
	if(res_config_file != RESULT_OK) return res_config_file;
1140 dev 215
 
1163 dev 216
	if(file_cfg.password[0] == '\0') {
1140 dev 217
		fprintf(stderr, "Password is not set\n");
1145 dev 218
		return RESULT_ERROR;
1140 dev 219
	}
220
 
1163 dev 221
	/* save parsed values to general config */
222
	strncpy(cfg->config_file, cmd_cfg.config_file, sizeof(cfg->config_file));
223
 
224
	if(cmd_cfg.port > 0)
225
		cfg->port = cmd_cfg.port;
226
	else if(file_cfg.port > 0)
227
		cfg->port = file_cfg.port;
1140 dev 228
	else
1143 dev 229
		cfg->port = DEFAULT_PORT;
1140 dev 230
 
1163 dev 231
	strncpy(cfg->password, file_cfg.password, sizeof(cfg->password));
232
 
1145 dev 233
	return RESULT_OK;
1140 dev 234
}
235
 
1163 dev 236
static int parse_cmd_line(struct config *cfg, int argc, const char* argv[])
1140 dev 237
{
238
	char *end;
239
	int  i;
1163 dev 240
	long port;
1140 dev 241
 
1163 dev 242
	memset(cfg, 0, sizeof(struct config));
1140 dev 243
 
244
	for(i = 1; i < argc; i++) {
245
		if(0 == strcmp(argv[i], PARAM_PORT_1) || 0 == strcmp(argv[i], PARAM_PORT_2)) {
1163 dev 246
			if(cfg->port > 0)
1145 dev 247
				return print_cmd_error(argc, argv, "Port is already set", NULL);
1140 dev 248
 
249
			if(++i < argc) {
1163 dev 250
				port = strtol(argv[i], &end, 10);
251
				if(*end != '\0' || port < PORT_MIN || port > PORT_MAX)
1145 dev 252
					return print_cmd_error(argc, argv, 
253
						"Port number must be integer between 1 and 65535", NULL);
1163 dev 254
				cfg->port = (ushort)port;
1140 dev 255
			}
256
			else {
1145 dev 257
				return print_cmd_error(argc, argv, "Port number expected", NULL);
1140 dev 258
			}
259
		}
260
		else if(0 == strcmp(argv[i], PARAM_CONFIG_1) || 0 == strcmp(argv[i], PARAM_CONFIG_2)) {
1163 dev 261
			if(cfg->config_file[0] != '\0')
1145 dev 262
				return print_cmd_error(argc, argv, "Config file is already set", NULL);
1140 dev 263
 
264
			if(++i < argc) {
1163 dev 265
				strncpy(cfg->config_file, argv[i], MAX_CONFIG_LINE);
266
				if(cfg->config_file[MAX_CONFIG_LINE - 1] != '\0')
1155 dev 267
					return print_cmd_error(argc, argv,
268
						"Config file name is too long", NULL);
1140 dev 269
			}
270
			else {
1145 dev 271
				return print_cmd_error(argc, argv, "Config file expected", NULL);
1140 dev 272
			}
273
		}
1145 dev 274
		else if(0 == strcmp(argv[i], PARAM_VERSION_1) || 0 == strcmp(argv[i], PARAM_VERSION_2)) {
275
			print_version();
276
			return RESULT_EXIT;
277
		}
278
		else if(0 == strcmp(argv[i], PARAM_HELP_1) || 0 == strcmp(argv[i], PARAM_HELP_2)) {
279
			pring_usage(argc, argv);
280
			return RESULT_EXIT;
281
		}
1140 dev 282
		else {
1145 dev 283
			return print_cmd_error(argc, argv, "Unknown parameter", argv[i]);
1140 dev 284
		}
285
	}
286
 
1145 dev 287
	return RESULT_OK;
1140 dev 288
}
289
 
290
 
1163 dev 291
static int parse_config_file(struct config *cfg, const char *config_name)
1140 dev 292
{
293
	FILE *config_file;
294
	char buf[MAX_CONFIG_LINE];
295
	char *line;
296
	char *subline;
297
	char *cur;
298
	int  count;
1156 dev 299
	uint len;
1163 dev 300
	uint password_len;
301
	long port;
1140 dev 302
 
1163 dev 303
	memset(cfg, 0, sizeof(struct config));
304
 
1140 dev 305
	config_file = fopen(config_name, "r");
306
	if(!config_file) {
307
		fprintf(stderr, "Can not open config file %s: %s\n", config_name, strerror(errno));
1145 dev 308
		return RESULT_ERROR;
1140 dev 309
	}
310
 
311
	count = 0;
312
	while(fgets(buf, sizeof(buf), config_file)) {
313
		count++;
314
		line = buf;
315
 
316
		/* skip end spaces */
1156 dev 317
		len = strlen(line);
1146 dev 318
		if(len == MAX_CONFIG_LINE-1)
319
			return print_config_error(config_name, count, "Line is too long", NULL);
1140 dev 320
		while(len && isspace(line[len-1])) --len;
321
		if(!len) continue;
322
		line[len] = '\0';
323
 
324
		/* skip begin spaces */
325
		while(line[0] != '\0' && isspace(line[0])) line++;
326
 
327
		if('#' == line[0]) { /* skip comment lines */
328
			continue;
329
		}
330
		else if(strncmp(line, CONFIG_PORT, min(sizeof(CONFIG_PORT) - 1, len)) == 0) {
1146 dev 331
			subline = line + sizeof(CONFIG_PORT) - 1;
1145 dev 332
			if('=' != subline[0] && !isspace(subline[0]))
333
				return print_config_error(config_name, count, "Unknown config parameter", line);
1140 dev 334
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
335
			if('=' != subline[0])
336
				return print_config_error(config_name, count, "Equal sign expected", NULL);
337
			subline++;
1163 dev 338
			port = strtol(subline, &subline, 10);
339
			if(port < PORT_MIN || port > PORT_MAX)
1140 dev 340
				return print_config_error(config_name, count, 
341
					"Port number must be integer between 1 and 65535", NULL);
342
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
343
			if('\0' != subline[0] && '#' != subline[0])
344
				return print_config_error(config_name, count, "End of line expected", NULL);
1163 dev 345
			cfg->port = (ushort)port;
1140 dev 346
		}
347
		else if(strncmp(line, CONFIG_PASSWORD, min(sizeof(CONFIG_PASSWORD) - 1, len)) == 0) {
1146 dev 348
			subline = line + sizeof(CONFIG_PASSWORD) - 1;
1145 dev 349
			if('=' != subline[0] && !isspace(subline[0]))
350
				return print_config_error(config_name, count, "Unknown config parameter", line);
1140 dev 351
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
352
			if('=' != subline[0])
353
				return print_config_error(config_name, count, "Equal sign expected", NULL);
354
			subline++;
355
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
356
			if('"' != subline[0])
357
				return print_config_error(config_name, count, "Open quot expected", NULL);
358
			subline++;
359
 
1163 dev 360
			cur          = cfg->password;
361
			password_len = 0;
362
			while(password_len < sizeof(cfg->password) - 1 
363
				&& subline[0] != '\0' && subline[0] != '"')
364
			{
1140 dev 365
				cur[0] = subline[0];
366
				cur++;
367
				subline++;
1163 dev 368
				password_len++;
1140 dev 369
			}
370
 
371
			cur[0] = '\0';
372
			if('"' != subline[0])
373
				return print_config_error(config_name, count, "Close quot expected", NULL);
374
			subline++;
375
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
376
			if('\0' != subline[0] && '#' != subline[0])
377
				return print_config_error(config_name, count, "End of line expected", NULL);
378
		}
379
		else {
380
			return print_config_error(config_name, count, "Unknown config parameter", line);
381
		}
382
	}
383
 
384
	if(fclose(config_file) != 0) {
385
		fprintf(stderr, "Can not close config file %s: %s\n", config_name, strerror(errno));
1145 dev 386
		return RESULT_UNEXPECTED;
1140 dev 387
	}
388
 
1145 dev 389
	return RESULT_OK;
1140 dev 390
}
391
 
1156 dev 392
static int get_revision(void)
1145 dev 393
{
1156 dev 394
	int        r;
395
	const char *begin;
1145 dev 396
 
397
	begin = REVISION;
398
	while(begin[0] != '\0' && begin[0] != ':') begin++;
399
	if(begin[0] == '\0') return 0;
400
	begin++;
1156 dev 401
	r = (int)strtol(begin, (char**)NULL, 10);
1145 dev 402
 
403
	return r;
404
}
405
 
1156 dev 406
static int create_child(int soc)
1143 dev 407
{
408
	pid_t child;
409
 
410
	child = fork();
411
	if(child == -1) {
412
		fprintf(stderr, "Cannot fork: %s\n", strerror(errno));
1145 dev 413
		return RESULT_UNEXPECTED; /* an unexpected error */
1143 dev 414
	}
415
	else if(child > 0) {
1156 dev 416
		if(close(soc) != 0) {
1143 dev 417
			fprintf(stderr, "Cannot close socket: %s\n", strerror(errno));
1145 dev 418
			return RESULT_UNEXPECTED; /* an unexpected error */
1143 dev 419
		}
1145 dev 420
		return RESULT_EXIT; /* we are the parent */
1143 dev 421
	}
422
	else {
1145 dev 423
		return RESULT_OK; /* we are the child */
1143 dev 424
	}
425
}
426
 
427
static int close_descr(int d)
428
{
429
	if(close(d) != 0) {
430
		syslog(LOG_ERR, "Cannot close descriptor %d: %s\n", d, strerror(errno));
1145 dev 431
		return RESULT_UNEXPECTED;
1143 dev 432
	}
1145 dev 433
	return RESULT_OK;
1143 dev 434
}
435
 
1156 dev 436
int main(int argc, const char* argv[])
1140 dev 437
{
1156 dev 438
	int           soc;
1143 dev 439
	struct config cfg;
1140 dev 440
 
1143 dev 441
	/* get config */
1145 dev 442
	switch(read_config(&cfg, argc, argv)) {
1156 dev 443
		case RESULT_EXIT:       return EXIT_OK;           /* no error but exit */
444
		case RESULT_ERROR:      return EXIT_USER_ERROR;   /* user error */
445
		case RESULT_UNEXPECTED: return EXIT_UNEXPECTED;   /* unexpected error */
1145 dev 446
	}
1143 dev 447
 
448
	/* try to listen the port */
1156 dev 449
	if((soc = establish(cfg.port)) < 0) {
1140 dev 450
		fprintf(stderr, "Cannot listen to port %i\n", cfg.port);
1145 dev 451
		return EXIT_USER_ERROR;
1140 dev 452
	}
453
 
1143 dev 454
	/* fork and release the console */
1156 dev 455
	switch(create_child(soc)) {
456
		case RESULT_EXIT:       return EXIT_OK;           /* no error but exit */
457
		case RESULT_ERROR:      return EXIT_USER_ERROR;   /* user error */
458
		case RESULT_UNEXPECTED: return EXIT_UNEXPECTED;   /* unexpected error */
1143 dev 459
	}
1140 dev 460
 
1143 dev 461
	/* continue as first child */
462
	if(setsid() == -1) {
463
		fprintf(stderr, "Cannot create session: %s\n", strerror(errno));
1145 dev 464
		return EXIT_UNEXPECTED;
1143 dev 465
	}
466
 
467
	/* fork the second time */
1156 dev 468
	switch(create_child(soc)) {
469
		case RESULT_EXIT:       return EXIT_OK;           /* no error but exit */
470
		case RESULT_ERROR:      return EXIT_USER_ERROR;   /* user error */
471
		case RESULT_UNEXPECTED: return EXIT_UNEXPECTED;   /* unexpected error */
1143 dev 472
	}
473
 
474
	/* continue as final child, from now do not use console for error output */
475
	chdir("/");
476
	umask(0);
1145 dev 477
	if(close_descr(0) != RESULT_OK) return EXIT_UNEXPECTED;
478
	if(close_descr(1) != RESULT_OK) return EXIT_UNEXPECTED;
479
	if(close_descr(2) != RESULT_OK) return EXIT_UNEXPECTED;
1143 dev 480
 
481
	syslog(LOG_INFO, "listen on %d", cfg.port);
1156 dev 482
	listen_socket(&cfg, soc);
1143 dev 483
 
1156 dev 484
	return EXIT_OK; /* unreachedable */
1140 dev 485
}
486