Subversion Repositories general

Rev

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