Newer
Older
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/sha.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
SLDaemon *SLDaemon::instance_ = 0;
SLDaemon::SLDaemon() : socket_fs_(0) {}
SLDaemon &SLDaemon::Instance() {
if (instance_ == 0)
instance_ = new SLDaemon();
return *instance_;
}
this->port = 0;
this->verbose = 3;
}
void SLDaemon::usage(const char *argv0) const {
std::cerr << "usage: "
<< argv0
<< " [-f conffile] [-d scriptdir] [-h] [-v] [-l login]"
<< "[-p pass] [-H host] [-P port] [-V]"
<< std::endl
<< " -f conffile : read and load conf file." << std::endl
<< " -d scriptdir : Scripts directory." << std::endl
<< " -h : Print this usage." << std::endl
<< " -v : Verbose mode." << std::endl
<< " -p secret : Set pass to secret." << std::endl
<< " -H name : Set server host to name." << std::endl
<< " -P number : Set server port to number." << std::endl
<< " -V : Print version and exit." << std::endl;
}
SLDaemon::Options *SLDaemon::getoptions(int argc, char *argv[]) const {
Options opt, *mopt;
}
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-h")) {
usage(*argv);
}
else if (!strcmp(argv[i], "-v")) {
opt.verbose = 1;
}
else if (!strcmp(argv[i], "-V")) {
std::cout << "sl daemon version : " << VERSION << std::endl;
}
else if (!strcmp(argv[i], "-H")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -h.");
opt.server = string(argv[i]);
}
else if (!strcmp(argv[i], "-P")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -p.");
char *endptr;
opt.port = strtol(argv[i], &endptr, 10);
if (!(*argv[i] != '\0' && *endptr == '\0'))
throw Error(ERR_USAGE, "Unable to convert port to a number.");
}
else if (!strcmp(argv[i], "-f")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -f.");
opt.conffile = string(argv[i]);
}
else if (!strcmp(argv[i], "-d")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -d.");
throw Error(ERR_USAGE, "No enough argument for option -l.");
opt.login = string(argv[i]);
}
else if (!strcmp(argv[i], "-p")) {
if (++i >= argc)
throw Error(ERR_USAGE, "No enough argument for option -p.");
*err << "Invalid options : " << string(argv[i]) << ".";
throw *err;
}
}
SLDaemon::Options *SLDaemon::getoptions(const string file) const {
throw Error(ERR_BADPARAM, "Conf file not implemented");
throw Error(ERR_BADPARAM, "Conf file not implemented");
void SLDaemon::applyoptions(const Options *opt) {
if (opt->server != "") options_.server = opt->server;
if (opt->pass != "") options_.pass = opt->pass;
if (opt->verbose != 3) options_.verbose = opt->verbose;
if (opt->conffile != "") options_.conffile = opt->conffile;
if (opt->scriptdir != "") options_.scriptdir = opt->scriptdir;
}
void SLDaemon::check_options() const {
// print info in verbose mode
if (verbose()) {
std::cout << "Server host is : " << options_.server << "." << std::endl;
std::cout << "Server port is : " << options_.port << "." << std::endl;
std::cout << "Daemon login is : " << options_.login << "." << std::endl;
std::cout << "Daemon pass is : " << options_.pass << "." << std::endl;
std::cout << "Daemon scripts directory is : " << options_.scriptdir << "." << std::endl;
std::cout << "Verbose mode : " << verbose() << "." << std::endl;
}
// Check validy of arguement
if (options_.server == "")
throw Error(ERR_BADPARAM, "No server address specified.");
throw Error(ERR_BADPARAM, "No server port specified.");
throw Error(ERR_BADPARAM, "Bad server port number (1 - 65535).");
if (options_.login == "")
throw Error(ERR_BADPARAM, "No login specified.");
throw Error(ERR_BADPARAM, "No scripts directory specified.");
void SLDaemon::run() {
char *line;
check_options();
connect();
auth();
while (1) {
line = recvln();
// call right handler
try {
if (!strcmp(line, "EXIT\n"))
cmd_exit();
else if (!strcmp(line, "RELOAD\n"))
cmd_reload();
else if (!strcmp(line, "VERSION\n"))
cmd_version();
else if (!strcmp(line, "STATUS\n"))
cmd_status();
else if (!strcmp(line, "KILLALL\n"))
cmd_killall();
else if (!strcmp(line, "KILL\n"))
cmd_kill(line);
else if (!strncmp(line, "FILE ", 5))
cmd_file(line);
else
proto_violation();
}
catch (const Error &e) {
e.print();
send("!! ");
send(e.message());
}
delete line;
}
}
//******************************************************************************
// network functions
//******************************************************************************
void SLDaemon::connect() {
struct sockaddr_in daddr;
struct hostent *h;
disconnect();
// retrieve remote host info
h = gethostbyname(options_.server.c_str());
// create socket
socket_fd_ = socket(PF_INET, SOCK_STREAM, 0);
if (socket_fd_ == -1)
throw Error(ERR_NET, strerror(errno));
daddr.sin_family = AF_INET;
daddr.sin_port = htons(options_.port);
daddr.sin_addr = *((struct in_addr *) h->h_addr);
memset(daddr.sin_zero, '\0', sizeof daddr.sin_zero);
// connect
if (::connect(socket_fd_, (struct sockaddr *) &daddr, sizeof daddr) == -1)
throw Error(ERR_NET, strerror(errno));
if ((socket_fs_ = fdopen(socket_fd_, "r+")) == 0)
if (fclose(socket_fs_))
throw Error(ERR_NET, strerror(errno));
socket_fs_ = 0;
}
void SLDaemon::send(long int i) {
fprintf(socket_fs_, "%li", i);
void SLDaemon::send(const string str, bool buf) {
send(str.c_str(), str.length(), buf);
}
void SLDaemon::send(const char *data, size_t len, bool buf) {
if (fwrite(data, 1, len, socket_fs_) != len)
throw Error(ERR_NET, strerror(errno));
if (!buf && fflush(socket_fs_))
throw Error(ERR_NET, strerror(errno));
char *SLDaemon::recv(size_t size) {
char *data = new char[size];
if (fread(data, 1, size, socket_fs_) != size) {
delete data;
throw Error(ERR_NET, strerror(errno));
char *data = recv(size);
FILE *fs = fopen(filename.c_str(), "w");
if (fs == 0)
throw Error(ERR_FILE, (string) "recv: " + strerror(errno));
throw Error(ERR_FILE, (string) "recv: " + strerror(errno));
throw Error(ERR_FILE, (string) "recv: " + strerror(errno));
char *SLDaemon::recvln() {
char *line = new char[MAX_LINE_SIZE];
if (fgets(line, MAX_LINE_SIZE, socket_fs_) == 0) {
throw Error(ERR_FILE, (string) "recvln: " + strerror(errno));
}
return line;
}
void SLDaemon::flush() {
if (fflush(socket_fs_))
throw Error(ERR_NET, strerror(errno));
//******************************************************************************
// protocol functions
//******************************************************************************
void SLDaemon::auth() {
unsigned char md[SHA_DIGEST_LENGTH];
BIO *bmem, *b64;
BUF_MEM *bptr;
SHA1((const unsigned char *) options_.pass.c_str(), options_.pass.length(), md);
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, md, SHA_DIGEST_LENGTH);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
memcpy(buff, bptr->data, bptr->length-1);
buff[bptr->length-1] = 0;
BIO_free_all(b64);
send(options_.login);
send("\n");
send("PASS ");
void SLDaemon::answer(const string &msg, bool prefix) {
if (verbose())
std::cout << (prefix ? SND_DATA : "") << msg;
send(msg);
}
void SLDaemon::answer(long int i, bool prefix) {
if (verbose()) std::cout << (prefix ? SND_DATA : "") << i;
send(i);
}
void SLDaemon::warn(const string &msg, const string &prefix) {
}
//******************************************************************************
// command functions
//******************************************************************************
exit(ERR_OK);
}
void SLDaemon::cmd_version() {
// Check if file exist
struct stat st;
int ret = lstat(path.c_str(), &st);
return;
}
// check file owner
if (st.st_uid != getuid()) {
if (pid == -1) {
answer((string) "JOB: Unable to fork: " + strerror(errno) + ".\n");
return;
}
// send job info
snprintf(buf, MAX_LINE_SIZE,
"JOB: %s, pid: %d, start at: %ld.\n",
j.name.c_str(), j.pid, j.start_time);
answer(buf);
execl(path.c_str(), basename(path.c_str()), 0);
}
}
void SLDaemon::cmd_file(const char *line) {
char filename[MAX_LINE_SIZE];
if (sscanf(line, "FILE %512s\n", filename) != 1) { //FIXME: bad magic size
proto_violation();
return;
}
string target = options_.scriptdir + "/" + filename;
//get size
int size;
buf = recvln();
ret = sscanf(buf, "SIZE %i\n", &size);
delete buf;
if (ret != 1) {
warn(buf);
ret = sscanf(buf, "MD5 %512s\n", md5); //FIXME: bad magic size
return;
}
//get data
try {
recv(size, target);
}
catch (const Error &err) {
if (err.code() == ERR_FILE) {
return;
}
throw;
}
// check MD5
if (SLDaemon::md5(target) != string(md5)) {
// proceed chown
if (chown(target.c_str(), getuid(), getgid())) {
unlink(target.c_str());
return;
}
// proceed chmod
if (chmod(target.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) {
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
void SLDaemon::cmd_update() {
char *buf;
int ret;
// get filename
const string &target = binpath;
//get size
int size;
buf = recvln();
warn(buf);
ret = sscanf(buf, "SIZE %i\n", &size);
delete buf;
if (ret != 1) {
answer("UPDATE: Invalid size parameter.\n");
return;
}
//get md5
char md5[MAX_LINE_SIZE];
buf = recvln();
warn(buf);
ret = sscanf(buf, "MD5 %512s\n", md5); //FIXME: bad magic size
delete buf;
if (ret != 1) {
answer("UPDATE: Invalid md5 parameter.\n");
return;
}
//get data
try {
recv(size, target);
}
catch (const Error &err) {
if (err.code() == ERR_FILE) {
answer("UPDATE: Data transfer error.\n");
return;
}
throw;
}
// check MD5
if (SLDaemon::md5(target) != string(md5)) {
answer("UPDATE: file " + target + ": Invalid MD5.\n");
unlink(target.c_str());
return;
}
// proceed chown
if (chown(target.c_str(), getuid(), getgid())) {
answer("FILE: chown of " + target + ": Unable to chown.\n");
unlink(target.c_str());
return;
}
// proceed chmod
if (chmod(target.c_str(), S_IRUSR | S_IWUSR | S_IXUSR)) {
answer("FILE: chmod of " + target + ": Unable to chmod.\n");
unlink(target.c_str());
return;
}
answer("UPDATE: Transfer of " + target + ": OK.\n");
void SLDaemon::cmd_list() {
FILE *fls = popen(string("ls -1A " + options_.scriptdir).c_str(), "r");
answer("LIST: Unable to list " + options_.scriptdir + ".\n");
return;
}
char buf[255];
size_t len;
try {
while ((len = fread(buf, 1, 255, fls)) > 0)
send(buf, len);
flush();
}
catch (...) {
pclose(fls);
throw;
}
}
void SLDaemon::cmd_status() {
t_job::iterator i;
time_t t = time(0);
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
snprintf(buf, MAX_LINE_SIZE,
" job: %s, pid: %d, start at: %ld, since: %ld seconds.\n",
i->name.c_str(), i->pid, i->start_time,t - i->start_time);
answer(buf);
}
void SLDaemon::cmd_kill(const char *line) {
t_job::iterator i;
// retrieve pid
int pid;
if (sscanf(line, "KILL %i\n", &pid) != 1) {
proto_violation();
return;
}
for (i = jobs_.begin(); i != jobs_.end(); ++i)
if (pid == i->pid) {
int ret = kill(i->pid, SIGKILL);
snprintf(buf, MAX_LINE_SIZE, "KILL: kill -SIGKILL %d (%s), return %d (%s).\n",
i->pid, i->name.c_str(), ret, ((ret == -1) ? strerror(errno) : "OK" ));
answer(buf);
}
flush();
}
void SLDaemon::cmd_killall() {
t_job::iterator i;
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
int ret = kill(i->pid, SIGKILL);
snprintf(buf, MAX_LINE_SIZE, "KILL: kill -SIGKILL %d (%s), return %d (%s).\n",
i->pid, i->name.c_str(), ret, ((ret == -1) ? strerror(errno) : "OK" ));
answer(buf);
return;
}
// FIXME: check re run right (exec + file owner)
answer((string) "RELOAD: Trying to reload `" + path.c_str() + "'.\n");
return;
}
// father process
if (pid) {
disconnect();
exit(0);
}
// son process
else {
execl(path.c_str(), path.c_str());
answer((string) "RELOAD: execl: " + strerror(errno) + ".\n");
}
//******************************************************************************
// others functions
//******************************************************************************
MD5_CTX ctx;
FILE *fs;
size_t len;
char buf[512];
char md[MD5_DIGEST_LENGTH];
char digest[MD5_DIGEST_LENGTH * 2 + 1];
if (!MD5_Init(&ctx))
return "";
return "";
while ((len = fread(buf, 1, 512, fs)) > 0)
if (!MD5_Update(&ctx, buf, len))
break;
if (!MD5_Final((unsigned char*)md, &ctx))
return "";
for(len = 0; len < MD5_DIGEST_LENGTH; ++len) {
sprintf(digest + (len * 2), "%02x", (unsigned char) md[len]);
}
digest[MD5_DIGEST_LENGTH * 2] = 0;
return string(digest);
//******************************************************************************
// Class Job functions
//******************************************************************************
SLDaemon::Job::Job(int p, const string &s, time_t t) : pid(p), name(s), start_time(t) {}
bool SLDaemon::Job::operator()(const SLDaemon::Job &a, const SLDaemon::Job &b) {
return a.pid < b.pid;
}
//******************************************************************************
// Friends Class functions
//******************************************************************************
void sigchild(int) {
int status;
SLDaemon &d = SLDaemon::Instance();
typedef SLDaemon::t_job t_job;
t_job::iterator i;
t_job &jobs_ = d.jobs_;
pid_t pid;
// Retrieve a pid
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
time_t t = time(0);
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
if (i->pid == pid) {
char buf[MAX_LINE_SIZE];
snprintf(buf, MAX_LINE_SIZE,
"JOB: %s, pid: %d, end at: %ld, since: %ld, return: %d\n",
i->name.c_str(), i->pid, t, t - i->start_time,
WEXITSTATUS(status));
write(d.socket_fd_, buf, strlen(buf));