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, "JOB ", 5))
cmd_job(line);
else if (!strncmp(line, "FILE ", 5))
cmd_file(line);
else if (!strncmp(line, "UPDATE ", 6))
cmd_update(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));
void SLDaemon::recv(size_t size, const string filename) {
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::tee(long int i) {
if (verbose()) std::cout << i;
send(i);
}
//******************************************************************************
// command functions
//******************************************************************************
std::cout << "EXIT requested." << std::endl;
send("Bye.\n");
exit(ERR_OK);
}
void SLDaemon::cmd_version() {
if (verbose())
std::cout << "VERSION requested." << std::endl;
send(VERSION);
send("\n");
// build path
//FIXME: Bad static magic number
char filename[512];
sscanf(line, "JOB %s\n", filename);
string path = options_.scriptdir + "/" + filename;
// Check if file exist and exec flag
struct stat buf;
int ret = lstat(path.c_str(), &buf);
if (ret == ENOENT || errno == EACCES) {
tee(strerror(errno));
// reg job
SLDaemon::Job j(pid, filename, time(0));
jobs_.insert(j);
// show job info
tee((string) "EXEC " + filename + ", pid: ");
tee(pid);
tee(", start at: ");
tee(j.start_time);
tee("\n");
execl(path.c_str(), basename(path.c_str()), 0);
printf("\"%s\"\n", path.c_str());
perror("sld: execl");
exit(ERR_UNKNOWN);
}
}
void SLDaemon::cmd_file(const char *line) {
// get filename
char filename[512]; //FIXME: bad magic size
if (sscanf(line, "FILE %512s\n", filename) != 1) {
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) {
tee("Invalid size parameter.");
return;
}
//get md5
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
delete buf;
if (ret != 1) {
tee("Invalid md5 parameter.");
return;
}
// show verbose
if (verbose())
std::cout << "FILE transfer requested: "
<< "to=" << target
<< ", size=" << size
<< ", md5=" << md5 << "."
<< std::endl;
//get data
try {
recv(size, target);
}
catch (const Error &err) {
if (err.code() == ERR_FILE) {
tee("Data transfer error.");
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)) {
unlink(target.c_str());
return;
}
tee("Transfer of " + target + ": OK.");
}
void SLDaemon::cmd_update(const char *line) {
assert(line);
if (verbose())
std::cout << "UPDATE requested." << std::endl;
void SLDaemon::cmd_list() {
if (verbose())
std::cout << "LIST requested." << std::endl;
FILE *fls = popen(string("ls -1A " + options_.scriptdir).c_str(), "r");
tee("Unable to list " + options_.scriptdir + ".");
return;
}
char buf[255];
size_t len;
try {
while ((len = fread(buf, 1, 255, fls)) > 0)
send(buf, len);
flush();
}
catch (...) {
pclose(fls);
throw;
}
}
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
581
582
583
584
585
586
587
588
void SLDaemon::cmd_status() {
t_job::iterator i;
if (verbose())
std::cout << "STATUS requested." << std::endl;
time_t t = time(0);
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
send("EXEC: ");
send(i->name);
send(", pid: ");
send(i->pid);
send(", start at: ");
send(i->start_time);
send(", since: ");
send(t - i->start_time);
send(" seconds.\n");
}
flush();
}
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;
}
if (verbose())
std::cout << "KILL " << pid
<<" requested." << std::endl;
for (i = jobs_.begin(); i != jobs_.end(); ++i)
if (pid == i->pid) {
int ret = kill(i->pid, SIGKILL);
send("kill -SIGKILL ");
send(i->pid);
send(" (");
send(i->name);
send(") => ");
if (ret == -1)
send(strerror(errno));
else
send("0");
send("\n");
}
flush();
}
void SLDaemon::cmd_killall() {
t_job::iterator i;
std::cout << "KILLALL requested." << std::endl;
for (i = jobs_.begin(); i != jobs_.end(); ++i) {
int ret = kill(i->pid, SIGKILL);
send("kill -SIGKILL ");
send(i->pid);
send(" (");
send(i->name);
send(") => ");
if (ret == -1)
send(strerror(errno));
else
send("0");
send("\n");
}
flush();
}
void SLDaemon::cmd_reload() {
if (verbose())
std::cout << "RELOAD requested." << std::endl;
}
//******************************************************************************
// others functions
//******************************************************************************
string SLDaemon::md5(const string file) const {
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);
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
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));
jobs_.erase(i);
break;
}
}
}
}
//******************************************************************************
// Class Job functions
//******************************************************************************
SLDaemon::Job::Job(int p, 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;
}