You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
5.9 KiB
271 lines
5.9 KiB
/* |
|
* Copyright 2014 Con Kolivas |
|
* |
|
* This program is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License as published by the Free |
|
* Software Foundation; either version 3 of the License, or (at your option) |
|
* any later version. See COPYING for more details. |
|
*/ |
|
|
|
#include <sys/prctl.h> |
|
#include <sys/socket.h> |
|
#include <sys/stat.h> |
|
#include <sys/types.h> |
|
#include <signal.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
|
|
#include "ckpool.h" |
|
#include "libckpool.h" |
|
#include "generator.h" |
|
|
|
/* Only global variable, to be used only by sighandler */ |
|
static ckpool_t *global_ckp; |
|
|
|
static void rename_proc(const char *name) |
|
{ |
|
char buf[16]; |
|
|
|
snprintf(buf, 16, "ckp@%s", name); |
|
prctl(PR_SET_NAME, buf, 0, 0, 0); |
|
} |
|
|
|
static void *listener(void *arg) |
|
{ |
|
proc_instance_t *pi = (proc_instance_t *)arg; |
|
unixsock_t *us = &pi->us; |
|
int sockd; |
|
|
|
rename_proc(pi->sockname); |
|
retry: |
|
sockd = accept(us->sockd, NULL, NULL); |
|
if (sockd < 0) { |
|
if (interrupted()) |
|
goto retry; |
|
LOGERR("Failed to accept on socket in listener"); |
|
goto out; |
|
} |
|
/* Insert parsing and repeat code here */ |
|
out: |
|
if (sockd >= 0) |
|
close(sockd); |
|
close_unix_socket(us->sockd, us->path); |
|
return NULL; |
|
} |
|
|
|
/* Open the file in path, check if there is a pid in there that still exists |
|
* and if not, write the pid into that file. */ |
|
static bool write_pid(const char *path, pid_t pid) |
|
{ |
|
struct stat statbuf; |
|
FILE *fp; |
|
int ret; |
|
|
|
if (!stat(path, &statbuf)) { |
|
int oldpid; |
|
|
|
LOGWARNING("File %s exists", path); |
|
fp = fopen(path, "r"); |
|
if (!fp) { |
|
LOGERR("Failed to open file %s", path); |
|
return false; |
|
} |
|
ret = fscanf(fp, "%d", &oldpid); |
|
fclose(fp); |
|
if (ret == 1 && !(kill(oldpid, 0))) { |
|
LOGWARNING("Process %s pid %d still exists", path, oldpid); |
|
return false; |
|
} |
|
} |
|
fp = fopen(path, "w"); |
|
if (!fp) { |
|
LOGERR("Failed to open file %s", path); |
|
return false; |
|
} |
|
fprintf(fp, "%d", pid); |
|
fclose(fp); |
|
|
|
return true; |
|
} |
|
|
|
static void create_process_unixsock(proc_instance_t *pi) |
|
{ |
|
unixsock_t *us = &pi->us; |
|
|
|
us->path = strdup(pi->ckp->socket_dir); |
|
realloc_strcat(&us->path, pi->sockname); |
|
LOGDEBUG("Opening %s", us->path); |
|
us->sockd = open_unix_server(us->path); |
|
if (unlikely(us->sockd < 0)) |
|
quit(1, "Failed to open %s socket", pi->sockname); |
|
} |
|
|
|
static void write_namepid(proc_instance_t *pi) |
|
{ |
|
char s[256]; |
|
|
|
pi->pid = getpid(); |
|
sprintf(s, "%s%s.pid", pi->ckp->socket_dir, pi->processname); |
|
if (!write_pid(s, pi->pid)) |
|
quit(1, "Failed to write %s pid %d", pi->processname, pi->pid); |
|
} |
|
|
|
static void rm_namepid(proc_instance_t *pi) |
|
{ |
|
char s[256]; |
|
|
|
sprintf(s, "%s%s.pid", pi->ckp->socket_dir, pi->processname); |
|
unlink(s); |
|
} |
|
|
|
static void launch_process(proc_instance_t *pi) |
|
{ |
|
pid_t pid; |
|
|
|
pid = fork(); |
|
if (pid < 0) |
|
quit(1, "Failed to fork %s in launch_process", pi->processname); |
|
if (!pid) { |
|
int ret; |
|
|
|
rename_proc(pi->processname); |
|
write_namepid(pi); |
|
create_process_unixsock(pi); |
|
ret = pi->process(pi); |
|
close_unix_socket(pi->us.sockd, pi->us.path); |
|
rm_namepid(pi); |
|
exit(ret); |
|
} |
|
} |
|
|
|
static void clean_up(ckpool_t *ckp) |
|
{ |
|
rm_namepid(&ckp->main); |
|
dealloc(ckp->socket_dir); |
|
} |
|
|
|
static void shutdown_children(ckpool_t *ckp, int sig) |
|
{ |
|
kill(ckp->generator.pid, sig); |
|
} |
|
|
|
static void sighandler(int sig) |
|
{ |
|
shutdown_children(global_ckp, sig); |
|
clean_up(global_ckp); |
|
exit(0); |
|
} |
|
|
|
static void json_get_string(char **store, json_t *val, const char *res) |
|
{ |
|
json_t *entry = json_object_get(val, res); |
|
const char *buf; |
|
|
|
*store = NULL; |
|
if (json_is_null(entry)) { |
|
LOGDEBUG("Json did not find entry %s", res); |
|
return; |
|
} |
|
if (!json_is_string(entry)) { |
|
LOGWARNING("Json entry %s is not string", res); |
|
return; |
|
} |
|
buf = json_string_value(entry); |
|
LOGDEBUG("Json found entry %s: %s", res, buf); |
|
*store = strdup(buf); |
|
} |
|
|
|
static void parse_config(ckpool_t *ckp) |
|
{ |
|
json_error_t err_val; |
|
json_t *json_conf; |
|
|
|
json_conf = json_load_file(ckp->config, JSON_DISABLE_EOF_CHECK, &err_val); |
|
if (!json_conf) { |
|
LOGWARNING("Json decode error for config file %s: (%d): %s", ckp->config, |
|
err_val.line, err_val.text); |
|
return; |
|
} |
|
json_get_string(&ckp->btcdurl, json_conf, "btcdurl"); |
|
json_get_string(&ckp->btcdauth, json_conf, "btcdauth"); |
|
json_get_string(&ckp->btcdpass, json_conf, "btcdpass"); |
|
json_decref(json_conf); |
|
} |
|
|
|
int main(int argc, char **argv) |
|
{ |
|
struct sigaction handler; |
|
pthread_t pth_listener; |
|
ckpool_t ckp; |
|
int c, ret; |
|
|
|
global_ckp = &ckp; |
|
memset(&ckp, 0, sizeof(ckp)); |
|
while ((c = getopt(argc, argv, "c:gn:s:")) != -1) { |
|
switch (c) { |
|
case 'c': |
|
ckp.config = optarg; |
|
break; |
|
case 'g': |
|
/* Launch generator only */ |
|
break; |
|
case 'n': |
|
ckp.name = optarg; |
|
break; |
|
case 's': |
|
ckp.socket_dir = strdup(optarg); |
|
break; |
|
} |
|
} |
|
|
|
if (!ckp.name) |
|
ckp.name = strdup("ckpool"); |
|
if (!ckp.config) |
|
ckp.config = strdup("ckpool.conf"); |
|
if (!ckp.socket_dir) |
|
ckp.socket_dir = strdup("/tmp/ckpool"); |
|
|
|
realloc_strcat(&ckp.socket_dir, "/"); |
|
|
|
/* Ignore sigpipe */ |
|
signal(SIGPIPE, SIG_IGN); |
|
|
|
ret = mkdir(ckp.socket_dir, 0700); |
|
if (ret && errno != EEXIST) |
|
quit(1, "Failed to make directory %s", ckp.socket_dir); |
|
|
|
parse_config(&ckp); |
|
|
|
ckp.main.ckp = &ckp; |
|
ckp.main.processname = strdup("main"); |
|
ckp.main.sockname = strdup("listener"); |
|
write_namepid(&ckp.main); |
|
create_process_unixsock(&ckp.main); |
|
|
|
create_pthread(&pth_listener, listener, &ckp.main); |
|
|
|
/* Launch separate processes from here */ |
|
ckp.generator.ckp = &ckp; |
|
ckp.generator.processname = strdup("generator"); |
|
ckp.generator.sockname = ckp.generator.processname; |
|
ckp.generator.process = &generator; |
|
launch_process(&ckp.generator); |
|
|
|
/* Install signal handlers only for the master process to be able to |
|
* shut down all child processes */ |
|
handler.sa_handler = &sighandler; |
|
handler.sa_flags = 0; |
|
sigemptyset(&handler.sa_mask); |
|
sigaction(SIGTERM, &handler, NULL); |
|
sigaction(SIGINT, &handler, NULL); |
|
|
|
/* Shutdown from here */ |
|
join_pthread(pth_listener); |
|
|
|
shutdown_children(&ckp, SIGTERM); |
|
clean_up(&ckp); |
|
|
|
return 0; |
|
}
|
|
|