4,11 → 4,12 |
* A daemon which waits on specified TCP port for special string |
* (password) and reboots the computer. |
* |
* Command line: rebootd [--port PORT] [--config CONFIG_FILE] |
* Command line: rebootd [--version] [--help] [--interface IP] [--port PORT] [--config CONFIG_FILE] |
* Defaults are port 19 and config /etc/rebootd.conf. |
* |
* Config file looks like: |
* -------------------------- |
* interface=192.168.0.1 |
* port=19 |
* password="some password" |
* -------------------------- |
67,17 → 68,21 |
#define PARAM_HELP_2 "--help" |
#define PARAM_VERSION_1 "-v" |
#define PARAM_VERSION_2 "--version" |
#define PARAM_INTERFACE_1 "-i" |
#define PARAM_INTERFACE_2 "--interface" |
#define PARAM_PORT_1 "-p" |
#define PARAM_PORT_2 "--port" |
#define PARAM_CONFIG_1 "-c" |
#define PARAM_CONFIG_2 "--config" |
#define CONFIG_INTERFACE "interface" |
#define CONFIG_PORT "port" |
#define CONFIG_PASSWORD "password" |
|
struct config { |
char config_file[FILENAME_MAX]; |
ushort port; |
char password[MAX_CONFIG_LINE]; |
char config_file[FILENAME_MAX]; |
struct in_addr interface; |
ushort port; |
char password[MAX_CONFIG_LINE]; |
}; |
|
static int parse_cmd_line(struct config *cfg, int argc, const char* argv[]); |
89,7 → 94,7 |
return (a < b) ? a : b; |
} |
|
static int establish(ushort port) |
static int establish(const struct config *cfg) |
{ |
int s; |
struct sockaddr_in sa; |
97,8 → 102,8 |
memset(&sa, 0, sizeof(struct sockaddr_in)); |
|
sa.sin_family = AF_INET; |
sa.sin_port = htons(port); |
sa.sin_addr.s_addr = INADDR_ANY; |
sa.sin_port = htons(cfg->port); |
sa.sin_addr.s_addr = (cfg->interface.s_addr ? cfg->interface.s_addr : INADDR_ANY); |
|
if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
return -1; |
169,12 → 174,12 |
print_version(); |
printf("\nA daemon which waits on specified TCP port for special"); |
printf(" string (password) and reboots the computer.\n\n"); |
printf("Usage: %s [--port PORT] [--config CONFIG_FILE]\n", argv[0]); |
printf("Usage: %s [--interface IP] [--port PORT] [--config CONFIG_FILE]\n", argv[0]); |
printf(" or: %s --version\n", argv[0]); |
printf(" or: %s --help\n\n", argv[0]); |
printf("Defaults are port 19 and config /etc/rebootd.conf.\n\n"); |
printf("Config file looks like:\n--------------------------\n"); |
printf("port=19\npassword=\"some password\"\n--------------------------\n\n"); |
printf("interface=192.168.0.1\nport=19\npassword=\"some password\"\n--------------------------\n\n"); |
printf("Then run from any other host:\necho -n \"some password\" | nc host_to_rebot 19\n"); |
} |
|
206,6 → 211,7 |
struct config cmd_cfg; |
struct config file_cfg; |
|
memset(cfg, 0, sizeof(struct config)); |
res_cmd_line = parse_cmd_line(&cmd_cfg, argc, argv); |
if(res_cmd_line != RESULT_OK) return res_cmd_line; |
|
221,6 → 227,11 |
/* save parsed values to general config */ |
strncpy(cfg->config_file, cmd_cfg.config_file, sizeof(cfg->config_file)); |
|
if(cmd_cfg.interface.s_addr) |
cfg->interface = cmd_cfg.interface; |
else if(file_cfg.interface.s_addr) |
cfg->interface = file_cfg.interface; |
|
if(cmd_cfg.port > 0) |
cfg->port = cmd_cfg.port; |
else if(file_cfg.port > 0) |
233,6 → 244,28 |
return RESULT_OK; |
} |
|
static int parse_interface(const char *s, char **end, struct in_addr *ip) |
{ |
char buf[MAX_CONFIG_LINE]; |
int count; |
const char *c; |
|
c = s; |
count = 0; |
while(c[0] != '\0' && !isspace(c[0])) c++, count++; |
if(count > MAX_CONFIG_LINE) return RESULT_ERROR; |
strncpy(buf, s, count); |
buf[count] = '\0'; |
c++; |
|
if(end) *end = (char *)c; |
|
if(inet_aton(buf, ip) == 1) |
return RESULT_OK; |
else |
return RESULT_ERROR; |
} |
|
static int parse_cmd_line(struct config *cfg, int argc, const char* argv[]) |
{ |
char *end; |
242,8 → 275,21 |
memset(cfg, 0, sizeof(struct config)); |
|
for(i = 1; i < argc; i++) { |
if(0 == strcmp(argv[i], PARAM_PORT_1) || 0 == strcmp(argv[i], PARAM_PORT_2)) { |
if(cfg->port > 0) |
if(0 == strcmp(argv[i], PARAM_INTERFACE_1) || 0 == strcmp(argv[i], PARAM_INTERFACE_2)) { |
if(cfg->interface.s_addr) |
return print_cmd_error(argc, argv, "Interface is already set", NULL); |
|
if(++i < argc) { |
if(parse_interface(argv[i], (char **)NULL, &cfg->interface) != RESULT_OK) |
return print_cmd_error(argc, argv, |
"Cannot parse interface", argv[i]); |
} |
else { |
return print_cmd_error(argc, argv, "Interface expected", NULL); |
} |
} |
else if(0 == strcmp(argv[i], PARAM_PORT_1) || 0 == strcmp(argv[i], PARAM_PORT_2)) { |
if(cfg->port) |
return print_cmd_error(argc, argv, "Port is already set", NULL); |
|
if(++i < argc) { |
287,7 → 333,59 |
return RESULT_OK; |
} |
|
static int validate_equal_sign(const char *config_name, const int count, const char *line, |
uint name_len, char **subline) |
{ |
const char *c; |
|
c = line + name_len - 1; |
if('=' != c[0] && !isspace(c[0])) |
return print_config_error(config_name, count, "Unknown config parameter", line); |
while(c[0] != '\0' && isspace(c[0])) c++; |
if('=' != c[0]) |
return print_config_error(config_name, count, "Equal sign expected", NULL); |
c++; |
|
if(subline) *subline = (char *)c; |
|
return RESULT_OK; |
} |
|
static int validate_eol(const char *config_name, const int count, const char *line) |
{ |
while(line[0] != '\0' && isspace(line[0])) line++; |
if('\0' != line[0] && '#' != line[0]) |
return print_config_error(config_name, count, "End of line expected", NULL); |
|
return RESULT_OK; |
} |
|
static int extract_quoted(const char *config_name, const int count, char **subline, char *string, uint len) |
{ |
uint cur_len; |
|
while(*subline[0] != '\0' && isspace(*subline[0])) (*subline)++; |
if('"' != *subline[0]) |
return print_config_error(config_name, count, "Open quot expected", NULL); |
(*subline)++; |
|
cur_len = 0; |
while(cur_len < len && *subline[0] != '\0' && *subline[0] != '"') |
{ |
string[0] = *subline[0]; |
string++; |
(*subline)++; |
cur_len++; |
} |
|
string[0] = '\0'; |
if('"' != *subline[0]) |
return print_config_error(config_name, count, "Close quot expected", NULL); |
(*subline)++; |
|
return RESULT_OK; |
} |
|
static int parse_config_file(struct config *cfg, const char *config_name) |
{ |
FILE *config_file; |
294,11 → 392,10 |
char buf[MAX_CONFIG_LINE]; |
char *line; |
char *subline; |
char *cur; |
int count; |
uint len; |
uint password_len; |
long port; |
int res; |
|
memset(cfg, 0, sizeof(struct config)); |
|
327,59 → 424,44 |
if('#' == line[0]) { /* skip comment lines */ |
continue; |
} |
else if(strncmp(line, CONFIG_INTERFACE, min(sizeof(CONFIG_INTERFACE) - 1, len)) == 0) { |
if((res = validate_equal_sign(config_name, count, line, |
sizeof(CONFIG_INTERFACE), &subline)) != RESULT_OK) return res; |
|
if(parse_interface(subline, &subline, &cfg->interface) != RESULT_OK) |
return print_config_error(config_name, count, |
"Cannot parse interface", NULL); |
|
if((res = validate_eol(config_name, count, subline)) != RESULT_OK) return res; |
} |
else if(strncmp(line, CONFIG_PORT, min(sizeof(CONFIG_PORT) - 1, len)) == 0) { |
subline = line + sizeof(CONFIG_PORT) - 1; |
if('=' != subline[0] && !isspace(subline[0])) |
return print_config_error(config_name, count, "Unknown config parameter", line); |
while(subline[0] != '\0' && isspace(subline[0])) subline++; |
if('=' != subline[0]) |
return print_config_error(config_name, count, "Equal sign expected", NULL); |
subline++; |
if((res = validate_equal_sign(config_name, count, line, sizeof(CONFIG_PORT), |
&subline)) != RESULT_OK) return res; |
|
port = strtol(subline, &subline, 10); |
if(port < PORT_MIN || port > PORT_MAX) |
return print_config_error(config_name, count, |
"Port number must be integer between 1 and 65535", NULL); |
while(subline[0] != '\0' && isspace(subline[0])) subline++; |
if('\0' != subline[0] && '#' != subline[0]) |
return print_config_error(config_name, count, "End of line expected", NULL); |
|
if((res = validate_eol(config_name, count, subline)) != RESULT_OK) return res; |
cfg->port = (ushort)port; |
} |
else if(strncmp(line, CONFIG_PASSWORD, min(sizeof(CONFIG_PASSWORD) - 1, len)) == 0) { |
subline = line + sizeof(CONFIG_PASSWORD) - 1; |
if('=' != subline[0] && !isspace(subline[0])) |
return print_config_error(config_name, count, "Unknown config parameter", line); |
while(subline[0] != '\0' && isspace(subline[0])) subline++; |
if('=' != subline[0]) |
return print_config_error(config_name, count, "Equal sign expected", NULL); |
subline++; |
while(subline[0] != '\0' && isspace(subline[0])) subline++; |
if('"' != subline[0]) |
return print_config_error(config_name, count, "Open quot expected", NULL); |
subline++; |
if((res = validate_equal_sign(config_name, count, line, |
sizeof(CONFIG_PASSWORD), &subline)) != RESULT_OK) return res; |
|
cur = cfg->password; |
password_len = 0; |
while(password_len < sizeof(cfg->password) - 1 |
&& subline[0] != '\0' && subline[0] != '"') |
{ |
cur[0] = subline[0]; |
cur++; |
subline++; |
password_len++; |
} |
if((res = extract_quoted(config_name, count, &subline, |
cfg->password, sizeof(cfg->password))) != RESULT_OK) return res; |
|
cur[0] = '\0'; |
if('"' != subline[0]) |
return print_config_error(config_name, count, "Close quot expected", NULL); |
subline++; |
while(subline[0] != '\0' && isspace(subline[0])) subline++; |
if('\0' != subline[0] && '#' != subline[0]) |
return print_config_error(config_name, count, "End of line expected", NULL); |
if((res = validate_eol(config_name, count, subline)) != RESULT_OK) return res; |
} |
else { |
return print_config_error(config_name, count, "Unknown config parameter", line); |
} |
} |
if(ferror(config_file)) { |
fprintf(stderr, "Config file %s reading failed\n", config_name); |
} |
|
if(fclose(config_file) != 0) { |
fprintf(stderr, "Can not close config file %s: %s\n", config_name, strerror(errno)); |
446,7 → 528,7 |
} |
|
/* try to listen the port */ |
if((soc = establish(cfg.port)) < 0) { |
if((soc = establish(&cfg)) < 0) { |
fprintf(stderr, "Cannot listen to port %i\n", cfg.port); |
return EXIT_USER_ERROR; |
} |