Subversion Repositories general

Compare Revisions

Ignore whitespace Rev 1144 → Rev 1145

/rebootdaemon/trunk/rebootdaemon.c
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
}