1,6 → 1,7 |
/****************************************************************** |
* RebootDaemon. |
* |
* Daemon which waits on specified TCP port for special string |
* A daemon which waits on specified TCP port for special string |
* and reboots the computer. |
* |
* Command line: rebootdaemon [--port PORT] [--config CONFIG_FILE] |
37,23 → 38,42 |
#include <netinet/in.h> |
#include <arpa/inet.h> |
|
#define REVISION "$Rev$" |
/* name version number */ |
#define APP_NAME "RebootDaemon" |
#define VERSION "1.0" |
#define REVISION "$Rev$" |
|
#define DEFAULT_PORT 19 |
#define DEFAULT_CFG_FILE "/etc/rebootdaemon.conf" |
#define CMDSIZE 4096 |
#define BUFSIZE 4096 |
#define MAX_CONFIG_LINE 4096 |
#define PORT_MIN 1 |
#define PORT_MAX 65535 |
#define DEFAULT_PORT 19 |
#define DEFAULT_CFG_FILE "/etc/rebootdaemon.conf" |
#define CMDSIZE 4096 |
#define BUFSIZE 4096 |
#define MAX_CONFIG_LINE 4096 |
#define PORT_MIN 1 |
#define PORT_MAX 65535 |
|
#define PARAM_PORT_1 "-p" |
#define PARAM_PORT_2 "--port" |
#define PARAM_CONFIG_1 "-c" |
#define PARAM_CONFIG_2 "--config" |
#define CONFIG_PORT "port" |
#define CONFIG_PASSWORD "password" |
/* return values of functions */ |
#define RESULT_OK 0 |
#define RESULT_EXIT 1 |
#define RESULT_ERROR 2 |
#define RESULT_UNEXPECTED 3 |
|
/* return values for the whole program */ |
#define EXIT_OK 0 |
#define EXIT_USER_ERROR 1 |
#define EXIT_UNEXPECTED 2 |
|
/* command line and config file parameters */ |
#define PARAM_HELP_1 "-h" |
#define PARAM_HELP_2 "--help" |
#define PARAM_VERSION_1 "-v" |
#define PARAM_VERSION_2 "--version" |
#define PARAM_PORT_1 "-p" |
#define PARAM_PORT_2 "--port" |
#define PARAM_CONFIG_1 "-c" |
#define PARAM_CONFIG_2 "--config" |
#define CONFIG_PORT "port" |
#define CONFIG_PASSWORD "password" |
|
struct config { |
int port; |
char password[MAX_CONFIG_LINE]; |
61,6 → 81,7 |
|
static int parse_cmd_line(int argc, char* argv[], long *port, char *config_name); |
static int parse_config_file(char *config_name, long *port, char *password); |
static int get_revision(); |
|
static int min(int a, int b) |
{ |
135,13 → 156,33 |
} |
} |
|
static int print_cmd_error(char *msg, char *value) |
static void print_version() |
{ |
printf("%s %s.%d\n", APP_NAME, VERSION, get_revision()); |
} |
|
static void pring_usage(int argc, char* argv[]) |
{ |
print_version(); |
printf("\nA daemon which waits on specified TCP port for special string and reboots the computer.\n\n"); |
printf("Usage: %s [--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/rebootdaemon.conf.\n\n"); |
printf("Config file looks like:\n--------------------------\n"); |
printf("port=19\npassword=\"some password\"\n--------------------------\n\n"); |
printf("Then run from any other host:\necho -n \"some password\" | nc host_to_rebot 19\n"); |
} |
|
static int print_cmd_error(int argc, char* argv[], char *msg, char *value) |
{ |
fprintf(stderr, "%s", msg); |
if(value) fprintf(stderr, " - %s\n", value); |
fprintf(stderr, "\n"); |
|
return 0; |
pring_usage(argc, argv); |
|
return RESULT_ERROR; |
} |
|
static int print_config_error(char *config_name, int line, char *msg, char *value) |
150,24 → 191,28 |
if(value) fprintf(stderr, " - %s", value); |
fprintf(stderr, "\n"); |
|
return 0; |
return RESULT_ERROR; |
} |
|
/* return: 0 - no error, continue; 1 - no error, but exit; 2 - user error, exit; 3 - unexpected error, exit */ |
static int read_config(struct config *cfg, int argc, char* argv[]) |
{ |
int res_cmd_line; |
int res_config_file; |
long cmd_port; |
long file_port; |
char cmd_config[MAX_CONFIG_LINE]; |
|
if(!parse_cmd_line(argc, argv, &cmd_port, cmd_config)) |
return 0; |
res_cmd_line = parse_cmd_line(argc, argv, &cmd_port, cmd_config); |
if(res_cmd_line != RESULT_OK) return res_cmd_line; |
|
if(!parse_config_file(cmd_config[0] != '\0' ? cmd_config : DEFAULT_CFG_FILE, &file_port, cfg->password)) |
return 0; |
res_config_file = parse_config_file(cmd_config[0] != '\0' ? cmd_config : DEFAULT_CFG_FILE, |
&file_port, cfg->password); |
if(res_config_file != RESULT_OK) return res_config_file; |
|
if(cfg->password[0] == '\0') { |
fprintf(stderr, "Password is not set\n"); |
return 0; |
return RESULT_ERROR; |
} |
|
if(cmd_port > 0) |
177,7 → 222,7 |
else |
cfg->port = DEFAULT_PORT; |
|
return 1; |
return RESULT_OK; |
} |
|
static int parse_cmd_line(int argc, char* argv[], long *port, char *config_name) |
191,36 → 236,45 |
for(i = 1; i < argc; i++) { |
if(0 == strcmp(argv[i], PARAM_PORT_1) || 0 == strcmp(argv[i], PARAM_PORT_2)) { |
if(*port > 0) |
return print_cmd_error("Port is already set", NULL); |
return print_cmd_error(argc, argv, "Port is already set", NULL); |
|
if(++i < argc) { |
*port = strtol(argv[i], &end, 10); |
if(*end != '\0' || *port < PORT_MIN || *port > PORT_MAX) |
return print_cmd_error("Port number must be integer between 1 and 65535", NULL); |
return print_cmd_error(argc, argv, |
"Port number must be integer between 1 and 65535", NULL); |
} |
else { |
return print_cmd_error("Port number expected", NULL); |
return print_cmd_error(argc, argv, "Port number expected", NULL); |
} |
} |
else if(0 == strcmp(argv[i], PARAM_CONFIG_1) || 0 == strcmp(argv[i], PARAM_CONFIG_2)) { |
if(config_name[0] != '\0') |
return print_cmd_error("Config file is already set", NULL); |
return print_cmd_error(argc, argv, "Config file is already set", NULL); |
|
if(++i < argc) { |
strncpy(config_name, argv[i], MAX_CONFIG_LINE); |
if(config_name[MAX_CONFIG_LINE - 1] != '\0') |
return print_cmd_error("Config file name is too long", NULL); |
return print_cmd_error(argc, argv, "Config file name is too long", NULL); |
} |
else { |
return print_cmd_error("Config file expected", NULL); |
return print_cmd_error(argc, argv, "Config file expected", NULL); |
} |
} |
else if(0 == strcmp(argv[i], PARAM_VERSION_1) || 0 == strcmp(argv[i], PARAM_VERSION_2)) { |
print_version(); |
return RESULT_EXIT; |
} |
else if(0 == strcmp(argv[i], PARAM_HELP_1) || 0 == strcmp(argv[i], PARAM_HELP_2)) { |
pring_usage(argc, argv); |
return RESULT_EXIT; |
} |
else { |
return print_cmd_error("Unknown parameter", argv[i]); |
return print_cmd_error(argc, argv, "Unknown parameter", argv[i]); |
} |
} |
|
return 1; |
return RESULT_OK; |
} |
|
|
236,7 → 290,7 |
config_file = fopen(config_name, "r"); |
if(!config_file) { |
fprintf(stderr, "Can not open config file %s: %s\n", config_name, strerror(errno)); |
return 0; |
return RESULT_ERROR; |
} |
|
count = 0; |
257,6 → 311,8 |
continue; |
} |
else if(strncmp(line, CONFIG_PORT, min(sizeof(CONFIG_PORT) - 1, len)) == 0) { |
if('=' != subline[0] && !isspace(subline[0])) |
return print_config_error(config_name, count, "Unknown config parameter", line); |
subline = line + sizeof(CONFIG_PORT) - 1; |
while(subline[0] != '\0' && isspace(subline[0])) subline++; |
if('=' != subline[0]) |
271,6 → 327,8 |
return print_config_error(config_name, count, "End of line expected", NULL); |
} |
else if(strncmp(line, CONFIG_PASSWORD, min(sizeof(CONFIG_PASSWORD) - 1, len)) == 0) { |
if('=' != subline[0] && !isspace(subline[0])) |
return print_config_error(config_name, count, "Unknown config parameter", line); |
subline = line + sizeof(CONFIG_PASSWORD) - 1; |
while(subline[0] != '\0' && isspace(subline[0])) subline++; |
if('=' != subline[0]) |
303,12 → 361,26 |
|
if(fclose(config_file) != 0) { |
fprintf(stderr, "Can not close config file %s: %s\n", config_name, strerror(errno)); |
return 0; |
return RESULT_UNEXPECTED; |
} |
|
return 1; |
return RESULT_OK; |
} |
|
static int get_revision() |
{ |
int r; |
char *begin; |
|
begin = REVISION; |
while(begin[0] != '\0' && begin[0] != ':') begin++; |
if(begin[0] == '\0') return 0; |
begin++; |
r = strtol(begin, (char**)NULL, 10); |
|
return r; |
} |
|
static int create_child(int socket) |
{ |
pid_t child; |
316,17 → 388,17 |
child = fork(); |
if(child == -1) { |
fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); |
return 2; /* an unexpected error */ |
return RESULT_UNEXPECTED; /* an unexpected error */ |
} |
else if(child > 0) { |
if(close(socket) != 0) { |
fprintf(stderr, "Cannot close socket: %s\n", strerror(errno)); |
return 2; /* an unexpected error */ |
return RESULT_UNEXPECTED; /* an unexpected error */ |
} |
return 0; /* we are the parent */ |
return RESULT_EXIT; /* we are the parent */ |
} |
else { |
return 1; /* we are the child */ |
return RESULT_OK; /* we are the child */ |
} |
} |
|
334,9 → 406,9 |
{ |
if(close(d) != 0) { |
syslog(LOG_ERR, "Cannot close descriptor %d: %s\n", d, strerror(errno)); |
return 0; |
return RESULT_UNEXPECTED; |
} |
return 1; |
return RESULT_OK; |
} |
|
int main(int argc, char* argv[]) |
344,44 → 416,49 |
int socket; |
struct config cfg; |
|
|
/* get config */ |
if(!read_config(&cfg, argc, argv)) return 1; |
switch(read_config(&cfg, argc, argv)) { |
case RESULT_EXIT: return EXIT_OK; // no error but exit |
case RESULT_ERROR: return EXIT_USER_ERROR; // user error |
case RESULT_UNEXPECTED: return EXIT_UNEXPECTED; // unexpected error |
} |
|
/* try to listen the port */ |
if((socket = establish(cfg.port)) < 0) { |
fprintf(stderr, "Cannot listen to port %i\n", cfg.port); |
return 1; |
return EXIT_USER_ERROR; |
} |
|
/* fork and release the console */ |
switch(create_child(socket)) { |
case 0: return 0; |
case 2: return 2; |
case RESULT_EXIT: return EXIT_OK; // no error but exit |
case RESULT_ERROR: return EXIT_USER_ERROR; // user error |
case RESULT_UNEXPECTED: return EXIT_UNEXPECTED; // unexpected error |
} |
|
/* continue as first child */ |
if(setsid() == -1) { |
fprintf(stderr, "Cannot create session: %s\n", strerror(errno)); |
return 2; |
return EXIT_UNEXPECTED; |
} |
|
/* fork the second time */ |
switch(create_child(socket)) { |
case 0: return 0; |
case 2: return 2; |
case RESULT_EXIT: return EXIT_OK; // no error but exit |
case RESULT_ERROR: return EXIT_USER_ERROR; // user error |
case RESULT_UNEXPECTED: return EXIT_UNEXPECTED; // unexpected error |
} |
|
/* continue as final child, from now do not use console for error output */ |
chdir("/"); |
umask(0); |
if(!close_descr(0)) return 2; |
if(!close_descr(1)) return 2; |
if(!close_descr(2)) return 2; |
if(close_descr(0) != RESULT_OK) return EXIT_UNEXPECTED; |
if(close_descr(1) != RESULT_OK) return EXIT_UNEXPECTED; |
if(close_descr(2) != RESULT_OK) return EXIT_UNEXPECTED; |
|
syslog(LOG_INFO, "listen on %d", cfg.port); |
listen_socket(&cfg, socket); |
|
return 0; |
return EXIT_OK; // unreachedable |
} |
|