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>
const string VERSION = "1";
const int MAX_LINE_SIZE = 512;
const char *RCV_DATA = "<< ";
const char *SND_DATA = ">> ";
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));
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(const string &msg, const string &local_prefix) {
if (verbose())
std::cout << local_prefix << msg;
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
struct stat st;
int ret = lstat(path.c_str(), &st);
tee(strerror(errno), SND_DATA);
return;
}
// check for exec flag
if (st.st_mode | S_IXUSR != S_IXUSR) {
tee("JOB: no exec flag.\n", SND_DATA);
return;
}
// check file owner
if (st.st_uid != getuid()) {
tee("JOB: Bad file owner.\n", SND_DATA);
// reg job
SLDaemon::Job j(pid, filename, time(0));
jobs_.insert(j);
// show job info
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
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
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() {
FILE *fls = popen(string("ls -1A " + options_.scriptdir).c_str(), "r");
tee("LIST: Unable to list " + options_.scriptdir + ".\n", SND_DATA);
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) {
tee("job: ", SND_DATA);
tee(i->name);
tee(", pid: ");
tee(i->pid);
tee(", start at: ");
tee(i->start_time);
tee(", since: ");
tee(t - i->start_time);
tee(" seconds.\n");
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
}
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);
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;
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();
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
const char *path = binpath();
if (path == NULL) {
tee("RELOAD: Unable to locate sld binary.\n", SND_DATA);
return;
}
// Get bin info
struct stat st;
if (lstat(path, &st)) {
tee((string)"RELOAD: lstat: " + strerror(errno) + "\n", SND_DATA);
return;
}
// FIXME: check re run right (exec + file owner)
int pid = fork();
if (pid == -1) {
tee((string)"RELOAD: " + strerror(errno) + "\n", SND_DATA);
return;
}
// father process
if (pid) {
disconnect();
exit(0);
}
// son process
else {
execl(path, basename(path));
tee((string) "RELOAD: " + strerror(errno) + "\n", SND_DATA);
flush();
exit(ERR_UNKNOWN);
}
}
//******************************************************************************
// 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, 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
//******************************************************************************
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
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;
}
}
}
}