Rev 1177 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/******************************************************************
* rebootd.
*
* A daemon which waits on specified UDP port for special string
* (password) and reboots the computer.
*
* Run "./rebootd --help" to see options and parameters.
*
* Copyleft 2005 Anatoli Klassen
*
******************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/reboot.h>
#include "common.h"
#include "rebootd.h"
#include "config.h"
#include "signals.h"
#include "network.h"
int daemonized = 0; /* we are already a daemon */
volatile int reconfig_required = 0; /* we have to stop socket listening and reread config */
volatile int quit_required = 0; /* we have to stop socket listening and exit */
void handle_command(const struct config *cfg, const char *cmd, const int cmd_len)
{
if(0 != strncmp(cmd, cfg->password, cmd_len)) return; /* wrong password */
if(cfg->debug_mode) {
syslog(LOG_INFO, "REBOOT, debug mode");
}
else {
syslog(LOG_EMERG, "REBOOT");
sleep(5);
if(reboot(RB_AUTOBOOT) < 0) {
syslog(LOG_ERR, "cannot reboot, %s", strerror(errno));
}
}
}
static int save_pid(const char *pid_file, const pid_t pid)
{
int fd;
FILE *file;
if(!pid_file || pid_file[0] == '\0') return RESULT_OK;
unlink(pid_file);
fd = open(pid_file, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(fd < 0) {
syslog(LOG_ERR, "Can open PID file %s for write: %s\n", pid_file, strerror(errno));
return RESULT_ERROR;
}
file = fdopen(fd, "w");
if(!file) {
syslog(LOG_ERR, "Can open PID file %s for write: %s\n", pid_file, strerror(errno));
return RESULT_ERROR;
}
if(fprintf(file, "%d\n", pid) < 0) {
syslog(LOG_ERR, "Can write PID to file %s: %s\n", pid_file, strerror(errno));
return RESULT_ERROR;
}
if(fclose(file) != 0) {
syslog(LOG_ERR, "Can not close PID file %s: %s\n", pid_file, strerror(errno));
return RESULT_UNEXPECTED;
}
return RESULT_OK;
}
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;
}
static void delete_pid_file(const char *pid_file)
{
if(pid_file && pid_file[0] != '\0') {
if(unlink(pid_file) != 0) {
syslog(LOG_ERR, "Cannot delete PID file %s: %s\n", pid_file, strerror(errno));
}
}
}
static void check_return(int res, const struct config *cfg)
{
if(daemonized && res != RESULT_OK) {
delete_pid_file(cfg->pid_file);
syslog(LOG_WARNING, "quit");
}
switch(res) {
case RESULT_EXIT: exit(EXIT_OK); return; /* no error but exit */
case RESULT_ERROR: exit(EXIT_USER_ERROR); return; /* user error */
case RESULT_UNEXPECTED: exit(EXIT_UNEXPECTED); return; /* unexpected error */
}
}
int main(int argc, const char* argv[])
{
int soc;
int reinit_needed;
struct config cfg;
/* get config */
check_return(read_config(&cfg, argc, argv), &cfg);
/* try to listen the port */
if((soc = establish(&cfg)) < 0) {
fprintf(stderr, "Cannot listen to port %i\n", cfg.port);
return EXIT_USER_ERROR;
}
/* fork and release the console */
check_return(create_child(soc), &cfg);
/* continue as first child */
if(setsid() == -1) {
fprintf(stderr, "Cannot create session: %s\n", strerror(errno));
return EXIT_UNEXPECTED;
}
/* fork the second time */
check_return(create_child(soc), &cfg);
/* continue as final child */
if(init_signals() != RESULT_OK) return EXIT_UNEXPECTED;
if(chdir("/") < 0) {
fprintf(stderr, "Cannot chdir to /: %s\n", strerror(errno));
return EXIT_UNEXPECTED;
}
umask(0);
check_return(save_pid(cfg.pid_file, getpid()), &cfg);
daemonized = 1;
/* from now do not use console for error output */
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;
for(;;) {
syslog(LOG_INFO, "listen on %d", cfg.port);
listen_socket(&cfg, soc);
if(quit_required) {
break;
}
else if(reconfig_required) {
reconfig_required = 0;
check_return(reread_config(&cfg, &reinit_needed), &cfg);
if(reinit_needed) {
close(soc);
if((soc = establish(&cfg)) < 0) {
syslog(LOG_ERR, "Cannot listen to port %i\n", cfg.port);
check_return(RESULT_ERROR, &cfg);
}
}
}
}
delete_pid_file(cfg.pid_file);
return EXIT_OK;
}