Subversion Repositories general

Rev

Rev 1142 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1140 dev 1
/******************************************************************
2
 *
3
 * Daemon which waiting on specified TCP port for special string
4
 * and reboot the computer.
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
 
38
#define PORT             19
39
#define CFG_FILE         "/etc/rebootdaemon.conf"
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 struct config cfg;
59
 
60
static int parse_cmd_line(int argc, char* argv[], long *port, char *config_name);
61
static int parse_config_file(char *config_name, long *port, char *password);
62
 
63
static int min(int a, int b)
64
{
65
	return (a < b) ? a : b;
66
}
67
 
68
static int establish(unsigned short portnum)
69
{
70
	int s;
71
	struct sockaddr_in sa;
72
 
73
	memset(&sa, 0, sizeof(struct sockaddr_in));
74
 
75
	sa.sin_family      = AF_INET;
76
	sa.sin_port        = htons(portnum);
77
	sa.sin_addr.s_addr = INADDR_ANY;
78
 
79
	if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
80
		return -1;
81
 
82
	if(bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
83
		close(s);
84
		return -1;
85
	}
86
 
87
	listen(s, 1);
88
 
89
	return s;
90
}
91
 
92
static int print_cmd_error(char *msg, char *value)
93
{
94
	fprintf(stderr, "%s", msg);
95
	if(value) fprintf(stderr, " - %s\n", value);
96
	fprintf(stderr, "\n");
97
 
98
	return 0;
99
}
100
 
101
static int print_config_error(char *config_name, int line, char *msg, char *value)
102
{
103
	fprintf(stderr, "Error in %s, line %d: %s", config_name, line, msg);
104
	if(value) fprintf(stderr, " - %s", value);
105
	fprintf(stderr, "\n");
106
 
107
	return 0;
108
}
109
 
110
static int read_config(int argc, char* argv[])
111
{
112
	long cmd_port;
113
	long file_port;
114
	char cmd_config[MAX_CONFIG_LINE];
115
 
116
	if(!parse_cmd_line(argc, argv, &cmd_port, cmd_config)) return 0;
117
 
118
	if(!parse_config_file(cmd_config[0] != '\0' ? cmd_config : CFG_FILE, &file_port, cfg.password)) return 0;
119
 
120
	if(cfg.password[0] == '\0') {
121
		fprintf(stderr, "Password is not set\n");
122
		return 0;
123
	}
124
 
125
	if(cmd_port > 0)
126
		cfg.port = (int)cmd_port;
127
	else if(file_port > 0)
128
		cfg.port = (int)file_port;
129
	else
130
		cfg.port = PORT;
131
 
132
	return 1;
133
}
134
 
135
static int parse_cmd_line(int argc, char* argv[], long *port, char *config_name)
136
{
137
	char *end;
138
	int  i;
139
 
140
	*port          = 0;
141
	config_name[0] = '\0';
142
 
143
	for(i = 1; i < argc; i++) {
144
		if(0 == strcmp(argv[i], PARAM_PORT_1) || 0 == strcmp(argv[i], PARAM_PORT_2)) {
145
			if(*port > 0)
146
				return print_cmd_error("Port is already set", NULL);
147
 
148
			if(++i < argc) {
149
				*port = strtol(argv[i], &end, 10);
150
				if(*end != '\0' || *port < PORT_MIN || *port > PORT_MAX)
151
					return print_cmd_error("Port number must be integer between 1 and 65535", NULL);
152
			}
153
			else {
154
				return print_cmd_error("Port number expected", NULL);
155
			}
156
		}
157
		else if(0 == strcmp(argv[i], PARAM_CONFIG_1) || 0 == strcmp(argv[i], PARAM_CONFIG_2)) {
158
			if(config_name[0] != '\0')
159
				return print_cmd_error("Config file is already set", NULL);
160
 
161
			if(++i < argc) {
162
				strncpy(config_name, argv[i], MAX_CONFIG_LINE);
163
				if(config_name[MAX_CONFIG_LINE - 1] != '\0')
164
					return print_cmd_error("Config file name is too long", NULL);
165
			}
166
			else {
167
				return print_cmd_error("Config file expected", NULL);
168
			}
169
		}
170
		else {
171
			return print_cmd_error("Unknown parameter", argv[i]);
172
		}
173
	}
174
 
175
	return 1;
176
}
177
 
178
 
179
static int parse_config_file(char *config_name, long *port, char *password)
180
{
181
	FILE *config_file;
182
	char buf[MAX_CONFIG_LINE];
183
	char *line;
184
	char *subline;
185
	char *cur;
186
	int  count;
187
 
188
	config_file = fopen(config_name, "r");
189
	if(!config_file) {
190
		fprintf(stderr, "Can not open config file %s: %s\n", config_name, strerror(errno));
191
		return 0;
192
	}
193
 
194
	count = 0;
195
	while(fgets(buf, sizeof(buf), config_file)) {
196
		count++;
197
		line = buf;
198
 
199
		/* skip end spaces */
200
		int len = strlen(line);
201
		while(len && isspace(line[len-1])) --len;
202
		if(!len) continue;
203
		line[len] = '\0';
204
 
205
		/* skip begin spaces */
206
		while(line[0] != '\0' && isspace(line[0])) line++;
207
 
208
		if('#' == line[0]) { /* skip comment lines */
209
			continue;
210
		}
211
		else if(strncmp(line, CONFIG_PORT, min(sizeof(CONFIG_PORT) - 1, len)) == 0) {
212
			subline = line + sizeof(CONFIG_PORT) - 1;
213
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
214
			if('=' != subline[0])
215
				return print_config_error(config_name, count, "Equal sign expected", NULL);
216
			subline++;
217
			*port = strtol(subline, &subline, 10);
218
			if(*port < PORT_MIN || *port > PORT_MAX)
219
				return print_config_error(config_name, count, 
220
					"Port number must be integer between 1 and 65535", NULL);
221
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
222
			if('\0' != subline[0] && '#' != subline[0])
223
				return print_config_error(config_name, count, "End of line expected", NULL);
224
		}
225
		else if(strncmp(line, CONFIG_PASSWORD, min(sizeof(CONFIG_PASSWORD) - 1, len)) == 0) {
226
			subline = line + sizeof(CONFIG_PASSWORD) - 1;
227
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
228
			if('=' != subline[0])
229
				return print_config_error(config_name, count, "Equal sign expected", NULL);
230
			subline++;
231
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
232
			if('"' != subline[0])
233
				return print_config_error(config_name, count, "Open quot expected", NULL);
234
			subline++;
235
 
236
			cur = password;
237
			while(subline[0] != '\0' && subline[0] != '"') {
238
				cur[0] = subline[0];
239
				cur++;
240
				subline++;
241
			}
242
 
243
			cur[0] = '\0';
244
			if('"' != subline[0])
245
				return print_config_error(config_name, count, "Close quot expected", NULL);
246
			subline++;
247
			while(subline[0] != '\0' && isspace(subline[0])) subline++;
248
			if('\0' != subline[0] && '#' != subline[0])
249
				return print_config_error(config_name, count, "End of line expected", NULL);
250
		}
251
		else {
252
			return print_config_error(config_name, count, "Unknown config parameter", line);
253
		}
254
	}
255
 
256
	if(fclose(config_file) != 0) {
257
		fprintf(stderr, "Can not close config file %s: %s\n", config_name, strerror(errno));
258
		return 0;
259
	}
260
 
261
	return 1;
262
}
263
 
264
int main(int argc, char* argv[])
265
{
266
	int                s, t;
267
	char               cmd[CMDSIZE];
268
	char               buf[BUFSIZE];
269
	char*              msg;
270
	int                br;
271
	struct sockaddr_in sa;
272
	socklen_t          sa_len;
273
 
274
	if(!read_config(argc, argv)) return 1;
275
 
276
	if((s = establish(cfg.port)) < 0) {
277
		fprintf(stderr, "Cannot listen to port %i\n", cfg.port);
278
		return 1;
279
	}
280
 
281
	for(;;) {
282
		sa_len = sizeof(sa);
283
		if((t = accept(s, (struct sockaddr *)&sa, &sa_len)) < 0) {
284
			if(errno == EINTR) /* EINTR might happen on accept(), */
285
				continue;        /* try again */
286
 
287
			msg = strerror(errno);
288
			syslog(LOG_ERR, "cannot get connection, %s", msg);
289
			continue;
290
		}
291
 
292
		msg = inet_ntoa(sa.sin_addr);
293
		syslog(LOG_INFO, "connect from %s", msg);
294
 
295
		cmd[0] = '\0';
296
		while((br = recv(t, buf, BUFSIZE, 0)) > 0) {
297
			strncat(cmd, buf, min(br, sizeof(cmd) - strlen(cmd) - 1));
298
		}
299
		sleep(1);
300
		close(t);
301
 
302
		syslog(LOG_INFO, "got command [%s]", cmd);
303
 
304
		if(0 == strncmp(cmd, cfg.password, sizeof(cmd))) {
305
			syslog(LOG_EMERG, "REBOOT");
306
			sleep(5);
307
			if(reboot(RB_AUTOBOOT) < 0) {
308
				msg = strerror(errno);
309
				syslog(LOG_ERR, "cannot reboot, %s", msg);
310
			}
311
		}
312
	}
313
 
314
	return 0;
315
}
316