Subversion Repositories general

Compare Revisions

No changes between revisions

Ignore whitespace Rev 1156 → Rev 1157

/rebootd/trunk/rebootdaemon.c
0,0 → 1,470
/******************************************************************
* RebootDaemon.
*
* A daemon which waits on specified TCP port for special string
* and reboots the computer.
*
* Command line: rebootdaemon [--port PORT] [--config CONFIG_FILE]
* Defaults are port 19 and config /etc/rebootdaemon.conf.
*
* Config file looks like:
* --------------------------
* port=19
* password="some password"
* --------------------------
*
* Usage example (from any other host):
* echo -n "some password" | nc host_to_rebot 19
*
* Copyleft 2005 Anatoli Klassen
*
******************************************************************/
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/reboot.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
/* 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
 
/* 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 {
ushort port;
char password[MAX_CONFIG_LINE];
};
 
static int parse_cmd_line(int argc, const char* argv[], long *port, char *config_name);
static int parse_config_file(const char *config_name, long *port, char *password);
static int get_revision(void);
 
static uint min(uint a, uint b)
{
return (a < b) ? a : b;
}
 
static int establish(ushort port)
{
int s;
struct sockaddr_in sa;
 
memset(&sa, 0, sizeof(struct sockaddr_in));
 
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = INADDR_ANY;
 
if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
 
if(bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
close(s);
return -1;
}
 
if(listen(s, 1) != 0) {
close(s);
return -1;
}
 
return s;
}
 
static void listen_socket(const struct config *cfg, int soc)
{
int t;
char cmd[CMDSIZE];
char buf[BUFSIZE];
int br;
struct sockaddr_in sa;
socklen_t sa_len;
int cmdlen;
 
for(;;) {
sa_len = sizeof(sa);
if((t = accept(soc, (struct sockaddr *)&sa, &sa_len)) < 0) {
if(errno == EINTR) /* EINTR might happen on accept(), */
continue; /* try again */
 
syslog(LOG_ERR, "cannot get connection, %s", strerror(errno));
continue;
}
 
syslog(LOG_INFO, "connect from %s", inet_ntoa(sa.sin_addr));
 
cmd[0] = '\0';
cmdlen = 0;
while((br = recv(t, buf, BUFSIZE, 0)) > 0) {
strncat(cmd, buf, min((uint)br, sizeof(cmd) - cmdlen - 1));
cmdlen += min((uint)br, sizeof(cmd) - cmdlen - 1);
}
sleep(1);
close(t);
 
syslog(LOG_INFO, "got command");
if(0 == strncmp(cmd, cfg->password, sizeof(cmd))) {
syslog(LOG_EMERG, "REBOOT");
sleep(5);
if(reboot(RB_AUTOBOOT) < 0) {
syslog(LOG_ERR, "cannot reboot, %s", strerror(errno));
}
}
}
}
 
static void print_version(void)
{
printf("%s %s.%d\n", APP_NAME, VERSION, get_revision());
}
 
static void pring_usage(int argc, const 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, const char* argv[], const char *msg, const char *value)
{
fprintf(stderr, "%s", msg);
if(value) fprintf(stderr, " - %s\n", value);
fprintf(stderr, "\n");
 
pring_usage(argc, argv);
 
return RESULT_ERROR;
}
 
static int print_config_error(const char *config_name, int line, const char *msg, const char *value)
{
fprintf(stderr, "Error in %s, line %d: %s", config_name, line, msg);
if(value) fprintf(stderr, " - %s", value);
fprintf(stderr, "\n");
 
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, const char* argv[])
{
int res_cmd_line;
int res_config_file;
long cmd_port;
long file_port;
char cmd_config[MAX_CONFIG_LINE];
 
res_cmd_line = parse_cmd_line(argc, argv, &cmd_port, cmd_config);
if(res_cmd_line != RESULT_OK) return res_cmd_line;
 
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 RESULT_ERROR;
}
 
if(cmd_port > 0)
cfg->port = (ushort)cmd_port;
else if(file_port > 0)
cfg->port = (ushort)file_port;
else
cfg->port = DEFAULT_PORT;
 
return RESULT_OK;
}
 
static int parse_cmd_line(int argc, const char* argv[], long *port, char *config_name)
{
char *end;
int i;
 
*port = 0;
config_name[0] = '\0';
 
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(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(argc, argv,
"Port number must be integer between 1 and 65535", NULL);
}
else {
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(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(argc, argv,
"Config file name is too long", NULL);
}
else {
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(argc, argv, "Unknown parameter", argv[i]);
}
}
 
return RESULT_OK;
}
 
 
static int parse_config_file(const char *config_name, long *port, char *password)
{
FILE *config_file;
char buf[MAX_CONFIG_LINE];
char *line;
char *subline;
char *cur;
int count;
uint len;
 
config_file = fopen(config_name, "r");
if(!config_file) {
fprintf(stderr, "Can not open config file %s: %s\n", config_name, strerror(errno));
return RESULT_ERROR;
}
 
count = 0;
while(fgets(buf, sizeof(buf), config_file)) {
count++;
line = buf;
 
/* skip end spaces */
len = strlen(line);
if(len == MAX_CONFIG_LINE-1)
return print_config_error(config_name, count, "Line is too long", NULL);
while(len && isspace(line[len-1])) --len;
if(!len) continue;
line[len] = '\0';
 
/* skip begin spaces */
while(line[0] != '\0' && isspace(line[0])) line++;
 
if('#' == line[0]) { /* skip comment lines */
continue;
}
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++;
*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);
}
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++;
 
cur = password;
while(subline[0] != '\0' && subline[0] != '"') {
cur[0] = subline[0];
cur++;
subline++;
}
 
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);
}
else {
return print_config_error(config_name, count, "Unknown config parameter", line);
}
}
 
if(fclose(config_file) != 0) {
fprintf(stderr, "Can not close config file %s: %s\n", config_name, strerror(errno));
return RESULT_UNEXPECTED;
}
 
return RESULT_OK;
}
 
static int get_revision(void)
{
int r;
const char *begin;
 
begin = REVISION;
while(begin[0] != '\0' && begin[0] != ':') begin++;
if(begin[0] == '\0') return 0;
begin++;
r = (int)strtol(begin, (char**)NULL, 10);
 
return r;
}
 
static int create_child(int soc)
{
pid_t child;
 
child = fork();
if(child == -1) {
fprintf(stderr, "Cannot fork: %s\n", strerror(errno));
return RESULT_UNEXPECTED; /* an unexpected error */
}
else if(child > 0) {
if(close(soc) != 0) {
fprintf(stderr, "Cannot close socket: %s\n", strerror(errno));
return RESULT_UNEXPECTED; /* an unexpected error */
}
return RESULT_EXIT; /* we are the parent */
}
else {
return RESULT_OK; /* we are the child */
}
}
 
static int close_descr(int d)
{
if(close(d) != 0) {
syslog(LOG_ERR, "Cannot close descriptor %d: %s\n", d, strerror(errno));
return RESULT_UNEXPECTED;
}
return RESULT_OK;
}
 
int main(int argc, const char* argv[])
{
int soc;
struct config cfg;
 
/* get config */
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((soc = establish(cfg.port)) < 0) {
fprintf(stderr, "Cannot listen to port %i\n", cfg.port);
return EXIT_USER_ERROR;
}
 
/* fork and release the console */
switch(create_child(soc)) {
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 EXIT_UNEXPECTED;
}
 
/* fork the second time */
switch(create_child(soc)) {
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) != 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, soc);
 
return EXIT_OK; /* unreachedable */
}
 
Property changes:
Added: svn:keywords
+Rev
\ No newline at end of property
/rebootd/trunk/Makefile
0,0 → 1,18
PROG= rebootdaemon
SRCS= rebootdaemon.c
 
rebootdaemon: $(SRCS)
cc -pedantic-errors -Wall -o $(PROG) $(SRCS)
 
all: clean rebootdaemon
 
pedantic:
lint -x -s -p -n -h -e -c -b -aa $(SRCS) | grep -E '^rebootdaemon.c'
cc -Wnested-externs -Wredundant-decls -Wmissing-declarations \
-Wmissing-prototypes -Wstrict-prototypes -Waggregate-return \
-Wwrite-strings -Wcast-align -Wcast-qual -Wpointer-arith \
-Wshadow -pedantic-errors -Wall -o $(PROG) $(SRCS)
 
clean:
rm -f $(PROG)
 
/rebootd/trunk/rebootdaemon.conf
0,0 → 1,5
# reboot daemon configuration
 
port=2019
password="password"
 
/rebootd/trunk/.
Property changes:
Added: svn:ignore
+rebootdaemon