Subversion Repositories general

Rev

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

Rev Author Line No. Line
1140 dev 1
/******************************************************************
2
 *
1143 dev 3
 * Daemon which waits on specified TCP port for special string
4
 * and reboots the computer.
1140 dev 5
 *
6
 * Command line: rebootdaemon [--port PORT] [--config CONFIG_FILE]
7
 * Defaults are port 19 and config /etc/rebootdaemon.conf.
8
 *
9
 * Config file looks like:
10
 * --------------------------
11
 * port=19
12
 * password="some password"
13
 * --------------------------
14
 *
15
 * Usage example (from any other host):
16
 * echo -n "some password" | nc host_to_rebot 19
17
 *
18
 * Copyleft 2005 Anatoli Klassen
19
 *
20
 ******************************************************************/
21
 
22
#include <stdlib.h>
23
#include <stdio.h>
24
#include <string.h>
25
#include <stdarg.h>
1144 dev 26
#include <ctype.h>
1140 dev 27
#include <errno.h>
28
#include <signal.h>
29
#include <unistd.h>
30
#include <netdb.h>
31
#include <syslog.h>
32
#include <sys/types.h>
1144 dev 33
#include <sys/stat.h>
1140 dev 34
#include <sys/socket.h>
35
#include <sys/wait.h>
36
#include <sys/reboot.h>
37
#include <netinet/in.h>
38
#include <arpa/inet.h>
39
 
1144 dev 40
#define REVISION         "$Rev: 1144 $"
41
 
1143 dev 42
#define DEFAULT_PORT     19
43
#define DEFAULT_CFG_FILE "/etc/rebootdaemon.conf"
1140 dev 44
#define CMDSIZE          4096
45
#define BUFSIZE          4096
46
#define MAX_CONFIG_LINE  4096
47
#define PORT_MIN         1
48
#define PORT_MAX         65535
49
 
50
#define PARAM_PORT_1     "-p"
51
#define PARAM_PORT_2     "--port"
52
#define PARAM_CONFIG_1   "-c"
53
#define PARAM_CONFIG_2   "--config"
54
#define CONFIG_PORT      "port"
55
#define CONFIG_PASSWORD  "password"
56
 
57
struct config {
58
	int  port;
59
	char password[MAX_CONFIG_LINE];
60
};
61
 
62
static int parse_cmd_line(int argc, char* argv[], long *port, char *config_name);
63
static int parse_config_file(char *config_name, long *port, char *password);
64
 
65
static int min(int a, int b)
66
{
67
	return (a < b) ? a : b;
68
}
69
 
70
static int establish(unsigned short portnum)
71
{
72
	int s;
73
	struct sockaddr_in sa;
74
 
75
	memset(&sa, 0, sizeof(struct sockaddr_in));
76
 
77
	sa.sin_family      = AF_INET;
78
	sa.sin_port        = htons(portnum);
79
	sa.sin_addr.s_addr = INADDR_ANY;
80
 
81
	if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
82
		return -1;
83
 
84
	if(bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
85
		close(s);
86
		return -1;
87
	}
88
 
89
	listen(s, 1);
90
 
91
	return s;
92
}
93
 
1143 dev 94
static void listen_socket(struct config *cfg, int socket)
1142 dev 95
{
96
	int                t;
97
	char               cmd[CMDSIZE];
98
	char               buf[BUFSIZE];
99
	char*              msg;
100
	int                br;
101
	struct sockaddr_in sa;
102
	socklen_t          sa_len;
103
 
104
	for(;;) {
105
		sa_len = sizeof(sa);
106
		if((t = accept(socket, (struct sockaddr *)&sa, &sa_len)) < 0) {
107
			if(errno == EINTR) /* EINTR might happen on accept(), */
108
				continue;        /* try again */
109
 
110
			msg = strerror(errno);
111
			syslog(LOG_ERR, "cannot get connection, %s", msg);
112
			continue;
113
		}
114
 
115
		msg = inet_ntoa(sa.sin_addr);
116
		syslog(LOG_INFO, "connect from %s", msg);
117
 
118
		cmd[0] = '\0';
119
		while((br = recv(t, buf, BUFSIZE, 0)) > 0) {
120
			strncat(cmd, buf, min(br, sizeof(cmd) - strlen(cmd) - 1));
121
		}
122
		sleep(1);
123
		close(t);
124
 
125
		syslog(LOG_INFO, "got command [%s]", cmd);
126
 
1143 dev 127
		if(0 == strncmp(cmd, cfg->password, sizeof(cmd))) {
1142 dev 128
			syslog(LOG_EMERG, "REBOOT");
129
			sleep(5);
130
			if(reboot(RB_AUTOBOOT) < 0) {
131
				msg = strerror(errno);
132
				syslog(LOG_ERR, "cannot reboot, %s", msg);
133
			}
134
		}
135
	}
136
}
137
 
1140 dev 138
static int print_cmd_error(char *msg, char *value)
139
{
140
	fprintf(stderr, "%s", msg);
141
	if(value) fprintf(stderr, " - %s\n", value);
142
	fprintf(stderr, "\n");
143
 
144
	return 0;
145
}
146
 
147
static int print_config_error(char *config_name, int line, char *msg, char *value)
148
{
149
	fprintf(stderr, "Error in %s, line %d: %s", config_name, line, msg);
150
	if(value) fprintf(stderr, " - %s", value);
151
	fprintf(stderr, "\n");
152
 
153
	return 0;
154
}
155
 
1143 dev 156
static int read_config(struct config *cfg, int argc, char* argv[])
1140 dev 157
{
158
	long cmd_port;
159
	long file_port;
160
	char cmd_config[MAX_CONFIG_LINE];
161
 
1143 dev 162
	if(!parse_cmd_line(argc, argv, &cmd_port, cmd_config))
163
		return 0;
1140 dev 164
 
1143 dev 165
	if(!parse_config_file(cmd_config[0] != '\0' ? cmd_config : DEFAULT_CFG_FILE, &file_port, cfg->password))
166
		return 0;
1140 dev 167
 
1143 dev 168
	if(cfg->password[0] == '\0') {
1140 dev 169
		fprintf(stderr, "Password is not set\n");
170
		return 0;
171
	}
172
 
173
	if(cmd_port > 0)
1143 dev 174
		cfg->port = (int)cmd_port;
1140 dev 175
	else if(file_port > 0)
1143 dev 176
		cfg->port = (int)file_port;
1140 dev 177
	else
1143 dev 178
		cfg->port = DEFAULT_PORT;
1140 dev 179
 
180
	return 1;
181
}
182
 
183
static int parse_cmd_line(int argc, char* argv[], long *port, char *config_name)
184
{
185
	char *end;
186
	int  i;
187
 
188
	*port          = 0;
189
	config_name[0] = '\0';
190
 
191
	for(i = 1; i < argc; i++) {
192
		if(0 == strcmp(argv[i], PARAM_PORT_1) || 0 == strcmp(argv[i], PARAM_PORT_2)) {
193
			if(*port > 0)
194
				return print_cmd_error("Port is already set", NULL);
195
 
196
			if(++i < argc) {
197
				*port = strtol(argv[i], &end, 10);
198
				if(*end != '\0' || *port < PORT_MIN || *port > PORT_MAX)
199
					return print_cmd_error("Port number must be integer between 1 and 65535", NULL);
200
			}
201
			else {
202
				return print_cmd_error("Port number expected", NULL);
203
			}
204
		}
205
		else if(0 == strcmp(argv[i], PARAM_CONFIG_1) || 0 == strcmp(argv[i], PARAM_CONFIG_2)) {
206
			if(config_name[0] != '\0')
207
				return print_cmd_error("Config file is already set", NULL);
208
 
209
			if(++i < argc) {
210
				strncpy(config_name, argv[i], MAX_CONFIG_LINE);
211
				if(config_name[MAX_CONFIG_LINE - 1] != '\0')
212
					return print_cmd_error("Config file name is too long", NULL);
213
			}
214
			else {
215
				return print_cmd_error("Config file expected", NULL);
216
			}
217
		}
218
		else {
219
			return print_cmd_error("Unknown parameter", argv[i]);
220
		}
221
	}
222
 
223
	return 1;
224
}
225
 
226
 
227
static int parse_config_file(char *config_name, long *port, char *password)
228
{
229
	FILE *config_file;
230
	char buf[MAX_CONFIG_LINE];
231
	char *line;
232
	char *subline;
233
	char *cur;
234
	int  count;
235
 
236
	config_file = fopen(config_name, "r");
237
	if(!config_file) {
238
		fprintf(stderr, "Can not open config file %s: %s\n", config_name, strerror(errno));
239
		return 0;
240
	}
241
 
242
	count = 0;
243
	while(fgets(buf, sizeof(buf), config_file)) {
244
		count++;
245
		line = buf;
246
 
247
		/* skip end spaces */
248
		int len = strlen(line);
249
		while(len && isspace(line[len-1])) --len;
250
		if(!len) continue;
251
		line[len] = '\0';
252
 
253
		/* skip begin spaces */
254
		while(line[0] != '\0' && isspace(line[0])) line++;
255
 
256
		if('#' == line[0]) { /* skip comment lines */
257
			continue;
258
		}
259
		else if(strncmp(line, CONFIG_PORT, min(sizeof(CONFIG_PORT) - 1, len)) == 0) {
260
			subline = line + sizeof(CONFIG_PORT) - 1;
261
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
262
			if('=' != subline[0])
263
				return print_config_error(config_name, count, "Equal sign expected", NULL);
264
			subline++;
265
			*port = strtol(subline, &subline, 10);
266
			if(*port < PORT_MIN || *port > PORT_MAX)
267
				return print_config_error(config_name, count, 
268
					"Port number must be integer between 1 and 65535", NULL);
269
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
270
			if('\0' != subline[0] && '#' != subline[0])
271
				return print_config_error(config_name, count, "End of line expected", NULL);
272
		}
273
		else if(strncmp(line, CONFIG_PASSWORD, min(sizeof(CONFIG_PASSWORD) - 1, len)) == 0) {
274
			subline = line + sizeof(CONFIG_PASSWORD) - 1;
275
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
276
			if('=' != subline[0])
277
				return print_config_error(config_name, count, "Equal sign expected", NULL);
278
			subline++;
279
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
280
			if('"' != subline[0])
281
				return print_config_error(config_name, count, "Open quot expected", NULL);
282
			subline++;
283
 
284
			cur = password;
285
			while(subline[0] != '\0' && subline[0] != '"') {
286
				cur[0] = subline[0];
287
				cur++;
288
				subline++;
289
			}
290
 
291
			cur[0] = '\0';
292
			if('"' != subline[0])
293
				return print_config_error(config_name, count, "Close quot expected", NULL);
294
			subline++;
295
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
296
			if('\0' != subline[0] && '#' != subline[0])
297
				return print_config_error(config_name, count, "End of line expected", NULL);
298
		}
299
		else {
300
			return print_config_error(config_name, count, "Unknown config parameter", line);
301
		}
302
	}
303
 
304
	if(fclose(config_file) != 0) {
305
		fprintf(stderr, "Can not close config file %s: %s\n", config_name, strerror(errno));
306
		return 0;
307
	}
308
 
309
	return 1;
310
}
311
 
1143 dev 312
static int create_child(int socket)
313
{
314
	pid_t child;
315
 
316
	child = fork();
317
	if(child == -1) {
318
		fprintf(stderr, "Cannot fork: %s\n", strerror(errno));
319
		return 2; /* an unexpected error */
320
	}
321
	else if(child > 0) {
322
		if(close(socket) != 0) {
323
			fprintf(stderr, "Cannot close socket: %s\n", strerror(errno));
324
			return 2; /* an unexpected error */
325
		}
326
		return 0; /* we are the parent */
327
	}
328
	else {
329
		return 1; /* we are the child */
330
	}
331
}
332
 
333
static int close_descr(int d)
334
{
335
	if(close(d) != 0) {
336
		syslog(LOG_ERR, "Cannot close descriptor %d: %s\n", d, strerror(errno));
337
		return 0;
338
	}
339
	return 1;
340
}
341
 
1140 dev 342
int main(int argc, char* argv[])
343
{
1143 dev 344
	int           socket;
345
	struct config cfg;
1140 dev 346
 
347
 
1143 dev 348
	/* get config */
349
	if(!read_config(&cfg, argc, argv)) return 1;
350
 
351
	/* try to listen the port */
352
	if((socket = establish(cfg.port)) < 0) {
1140 dev 353
		fprintf(stderr, "Cannot listen to port %i\n", cfg.port);
354
		return 1;
355
	}
356
 
1143 dev 357
	/* fork and release the console */
358
	switch(create_child(socket)) {
359
		case 0: return 0;
360
		case 2: return 2;
361
	}
1140 dev 362
 
1143 dev 363
	/* continue as first child */
364
	if(setsid() == -1) {
365
		fprintf(stderr, "Cannot create session: %s\n", strerror(errno));
366
		return 2;
367
	}
368
 
369
	/* fork the second time */
370
	switch(create_child(socket)) {
371
		case 0: return 0;
372
		case 2: return 2;
373
	}
374
 
375
	/* continue as final child, from now do not use console for error output */
376
	chdir("/");
377
	umask(0);
378
	if(!close_descr(0)) return 2;
379
	if(!close_descr(1)) return 2;
380
	if(!close_descr(2)) return 2;
381
 
382
	syslog(LOG_INFO, "listen on %d", cfg.port);
383
	listen_socket(&cfg, socket);
384
 
1140 dev 385
	return 0;
386
}
387