autostart ctorrent
This commit is contained in:
parent
8f0d6c1f54
commit
6301d8a640
2
Makefile
2
Makefile
@ -19,4 +19,4 @@ $(TARGET): $(SRC:.c=.o)
|
||||
clean:
|
||||
rm -rf $(TARGET) *.o *.d
|
||||
|
||||
-include $(shell find -name *.d 2> /dev/null)
|
||||
-include $(shell find . -name \*.d 2> /dev/null)
|
||||
|
87
connection.c
87
connection.c
@ -20,14 +20,19 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "configfile.h"
|
||||
#include "event.h"
|
||||
#include "httpd.h"
|
||||
#include "linebuffer.h"
|
||||
#include "list.h"
|
||||
#include "logging.h"
|
||||
#include "sockaddr.h"
|
||||
#include "spawn.h"
|
||||
#include "tcpsocket.h"
|
||||
|
||||
static LIST_HEAD(torrent_list);
|
||||
@ -39,7 +44,11 @@ struct torrent_file {
|
||||
/* list of clients in this cloud */
|
||||
struct list_head client_list;
|
||||
|
||||
/* name of torrentfile */
|
||||
char *name;
|
||||
|
||||
/* local seeder process */
|
||||
struct child_process *child;
|
||||
};
|
||||
|
||||
struct client_con {
|
||||
@ -94,6 +103,7 @@ static struct torrent_file * find_create_torrent(const char *fullpath)
|
||||
/* init fields */
|
||||
INIT_LIST_HEAD(&torrent->client_list);
|
||||
torrent->name = strdup(filename);
|
||||
torrent->child = NULL;
|
||||
|
||||
/* keep torrent list sorted by name */
|
||||
struct torrent_file *search;
|
||||
@ -105,6 +115,68 @@ static struct torrent_file * find_create_torrent(const char *fullpath)
|
||||
return torrent;
|
||||
}
|
||||
|
||||
static void child_exit(struct child_process *child, int exit_code, void *privdata)
|
||||
{
|
||||
struct torrent_file *torrent = (struct torrent_file *)privdata;
|
||||
log_print(LOG_INFO, "ctorrent [pid:%d] was killed (exit:%d)", child->pid, exit_code);
|
||||
torrent->child = NULL;
|
||||
}
|
||||
|
||||
static int null_read(int fd, void *privdata)
|
||||
{
|
||||
char buf[64];
|
||||
int len;
|
||||
|
||||
do {
|
||||
len = read(fd, buf, sizeof(buf));
|
||||
} while (len == sizeof(buf));
|
||||
|
||||
return !(len > 0);
|
||||
}
|
||||
|
||||
static int spawn_torrent_seeder(struct torrent_file *torrent)
|
||||
{
|
||||
// TODO: more than one search path?
|
||||
const char *path = config_get_string("global", "search-path", NULL);
|
||||
if (path == NULL) {
|
||||
log_print(LOG_WARN, "requesting torrentfile, but no search path given");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[256];
|
||||
int len = snprintf(buf, sizeof(buf), "%s/%s", path, torrent->name);
|
||||
if (len < 0 || len >= sizeof(buf)) {
|
||||
log_print(LOG_WARN, "filename > max");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
if (stat(buf, &statbuf) < 0) {
|
||||
log_print(LOG_WARN, "torrent file not found: %s", buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *ctorrent_bin = config_get_string("global", "ctorrent-bin", "/usr/bin/ctorrent");
|
||||
// TODO: statserv is not always localhost
|
||||
char *const args[] = { (char *)ctorrent_bin, "-S", "127.0.0.1:2780", "-f", buf, NULL };
|
||||
|
||||
torrent->child = alloc_child_process(args, path);
|
||||
if (spawn_child(torrent->child, child_exit, torrent) < 0) {
|
||||
log_print(LOG_ERROR, "spawn_child(%s)", args[0]);
|
||||
free_child_process(torrent->child);
|
||||
torrent->child = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_print(LOG_INFO, "spawned ctorrent for %s [pid:%d]", torrent->name, torrent->child->pid);
|
||||
|
||||
/* just read all output from ctorrent to /dev/null */
|
||||
event_add_readfd(NULL, torrent->child->fd[STDOUT_FILENO], null_read, NULL);
|
||||
event_add_readfd(NULL, torrent->child->fd[STDERR_FILENO], null_read, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_client(struct client_con *con)
|
||||
{
|
||||
list_del(&con->list);
|
||||
@ -204,16 +276,25 @@ int ctcs_trigger_status(void *privdata)
|
||||
|
||||
struct torrent_file *torrent;
|
||||
list_for_each_entry(torrent, &torrent_list, list) {
|
||||
int delete = 0;
|
||||
int seeder = 0;
|
||||
int leecher = 0;
|
||||
|
||||
struct client_con *con;
|
||||
list_for_each_entry(con, &torrent->client_list, list) {
|
||||
write(event_get_fd(con->event), "SENDSTATUS\n", 11);
|
||||
|
||||
delete += (con->completed == 0) ? -1 : 1;
|
||||
if (con->completed)
|
||||
seeder++;
|
||||
else
|
||||
leecher++;
|
||||
}
|
||||
|
||||
/* no seeders available and no local seeder running => spawn one */
|
||||
if (leecher > 0 && seeder == 0 && torrent->child == NULL)
|
||||
spawn_torrent_seeder(torrent);
|
||||
|
||||
/* delete holds the number of clients to quit */
|
||||
int delete = seeder - leecher;
|
||||
list_for_each_entry(con, &torrent->client_list, list) {
|
||||
if (delete <= 0)
|
||||
break;
|
||||
|
1
event.c
1
event.c
@ -255,7 +255,6 @@ int event_loop(void)
|
||||
list_del(&entry->list);
|
||||
free(entry);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (entry->flags & FD_READ) {
|
||||
|
210
spawn.c
Normal file
210
spawn.c
Normal file
@ -0,0 +1,210 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <pty.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "logging.h"
|
||||
#include "spawn.h"
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
static LIST_HEAD(child_proc_list);
|
||||
|
||||
struct child_process * alloc_child_process(char *const argv[], const char *pwd)
|
||||
{
|
||||
/* count args */
|
||||
int cnt = 0;
|
||||
while (argv[cnt++] != NULL);
|
||||
|
||||
/* alloc struct child_process and array of char **argv */
|
||||
int size = sizeof(struct child_process) + sizeof(char *) * cnt;
|
||||
struct child_process *child = malloc(size);
|
||||
if (child == NULL) {
|
||||
log_print(LOG_ERROR, "create_child_process(): malloc()");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(child, 0, sizeof(struct child_process));
|
||||
|
||||
/* argv points to first byte after struct child_process */
|
||||
child->argv = (char **)(child +1);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(child->fd); i++)
|
||||
child->fd[i] = -1;
|
||||
|
||||
/* copy argv */
|
||||
for (i = 0; i < cnt; i++)
|
||||
child->argv[i] = (argv[i] != NULL) ? strdup(argv[i]) : NULL;
|
||||
|
||||
/* copy pwd */
|
||||
if (pwd != NULL)
|
||||
child->pwd = strdup(pwd);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/* TODO: really export this to users? must not called when child still running */
|
||||
void free_child_process(struct child_process *child)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; child->argv[i] != NULL; i++)
|
||||
free(child->argv[i]);
|
||||
|
||||
if (child->pwd != NULL)
|
||||
free(child->pwd);
|
||||
|
||||
free(child);
|
||||
}
|
||||
|
||||
pid_t spawn_child(struct child_process *child, void (*exit_cb)(struct child_process *child, int exit_code, void *privdata), void *privdata)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
if (stat(child->argv[0], &stat_buf) != 0) {
|
||||
log_print(LOG_ERROR, "spawn_child(): stat()");
|
||||
return -1;
|
||||
|
||||
/* not a regular file, or not executable */
|
||||
} else if (!S_ISREG(stat_buf.st_mode) || !(stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
|
||||
log_print(LOG_ERROR, "spawn_child(): stat()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* exit callback */
|
||||
child->exit_cb = exit_cb;
|
||||
child->privdata = privdata;
|
||||
|
||||
/*
|
||||
* fd[0] - if -1 pipe() and dup2() read-side as child stdin, store write-side for parent
|
||||
* else dup2() this fd as child stdin
|
||||
* fd[1] - if -1 pipe() and dup2() write-side as child stdout, store read-side for parent
|
||||
* else dup2() this fd as child stdout
|
||||
* fd[2] - if -1 pipe() and dup2() write-side as child stderr, store read-side for parent
|
||||
* else dup2() this fd as child stderr
|
||||
*/
|
||||
int child_pipes[3][2];
|
||||
if (child->fd[STDIN_FILENO] == -1) {
|
||||
if (pipe(child_pipes[STDIN_FILENO]) < 0) {
|
||||
log_print(LOG_ERROR, "spawn_child(): pipe(STDIN_FILENO)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
child_pipes[STDIN_FILENO][0] = child->fd[STDIN_FILENO];
|
||||
child_pipes[STDIN_FILENO][1] = -1;
|
||||
}
|
||||
|
||||
if (child->fd[STDOUT_FILENO] == -1) {
|
||||
if (pipe(child_pipes[STDOUT_FILENO]) < 0) {
|
||||
log_print(LOG_ERROR, "spawn_child(): pipe(STDOUT_FILENO)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
child_pipes[STDOUT_FILENO][0] = -1;
|
||||
child_pipes[STDOUT_FILENO][1] = child->fd[STDOUT_FILENO];
|
||||
}
|
||||
|
||||
if (child->fd[STDERR_FILENO] == -1) {
|
||||
if (pipe(child_pipes[STDERR_FILENO]) < 0) {
|
||||
log_print(LOG_ERROR, "spawn_child(): pipe(STDERR_FILENO)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
child_pipes[STDERR_FILENO][0] = -1;
|
||||
child_pipes[STDERR_FILENO][1] = child->fd[STDERR_FILENO];
|
||||
}
|
||||
|
||||
/* add this to list now, so sigchld_handler can find it */
|
||||
list_add_tail(&child->list, &child_proc_list);
|
||||
|
||||
child->pid = fork();
|
||||
if (child->pid == 0) { /* child */
|
||||
close(child_pipes[STDIN_FILENO][1]);
|
||||
close(child_pipes[STDOUT_FILENO][0]);
|
||||
close(child_pipes[STDERR_FILENO][0]);
|
||||
|
||||
if (dup2(child_pipes[STDIN_FILENO][0], STDIN_FILENO) < 0) {
|
||||
perror("dup2(STDIN_FILENO)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (dup2(child_pipes[STDOUT_FILENO][1], STDOUT_FILENO) < 0) {
|
||||
perror("dup2(STDOUT_FILENO)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (dup2(child_pipes[STDERR_FILENO][1], STDERR_FILENO) < 0) {
|
||||
perror("dup2(STDERR_FILENO)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(child_pipes[STDIN_FILENO][0]);
|
||||
close(child_pipes[STDOUT_FILENO][1]);
|
||||
close(child_pipes[STDERR_FILENO][1]);
|
||||
|
||||
if (child->pwd != NULL && chdir(child->pwd) < 0) {
|
||||
perror("chdir()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
execv(child->argv[0], child->argv);
|
||||
perror("execv()");
|
||||
exit(1);
|
||||
|
||||
} else if (child->pid < 0) { /* fork error */
|
||||
log_print(LOG_ERROR, "spawn_child(): fork()");
|
||||
return -1;
|
||||
|
||||
} else { /* parent */
|
||||
if (child->fd[STDIN_FILENO] == -1) {
|
||||
close(child_pipes[STDIN_FILENO][0]);
|
||||
child->fd[STDIN_FILENO] = child_pipes[STDIN_FILENO][1];
|
||||
}
|
||||
|
||||
if (child->fd[STDOUT_FILENO] == -1) {
|
||||
close(child_pipes[STDOUT_FILENO][1]);
|
||||
child->fd[STDOUT_FILENO] = child_pipes[STDOUT_FILENO][0];
|
||||
}
|
||||
|
||||
if (child->fd[STDERR_FILENO] == -1) {
|
||||
close(child_pipes[STDERR_FILENO][1]);
|
||||
child->fd[STDERR_FILENO] = child_pipes[STDERR_FILENO][0];
|
||||
}
|
||||
|
||||
return child->pid;
|
||||
}
|
||||
}
|
||||
|
||||
void sigchld_handler(int sig)
|
||||
{
|
||||
struct child_process *child, *tmp;
|
||||
list_for_each_entry_safe(child, tmp, &child_proc_list, list) {
|
||||
int status = 0;
|
||||
int ret = waitpid(child->pid, &status, WNOHANG);
|
||||
if (ret == -1) {
|
||||
log_print(LOG_WARN, "sigchld_handler(): waitpid(%d)", child->pid);
|
||||
continue;
|
||||
|
||||
} else if (ret != 0 && WIFEXITED(status)) {
|
||||
list_del(&child->list);
|
||||
|
||||
if (child->exit_cb != NULL)
|
||||
child->exit_cb(child, WEXITSTATUS(status), child->privdata);
|
||||
|
||||
free_child_process(child);
|
||||
}
|
||||
}
|
||||
}
|
26
spawn.h
Normal file
26
spawn.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef _SPAWN_H_
|
||||
#define _SPAWN_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
struct child_process {
|
||||
struct list_head list;
|
||||
|
||||
char **argv;
|
||||
char *pwd;
|
||||
int fd[3];
|
||||
|
||||
pid_t pid;
|
||||
|
||||
void (*exit_cb)(struct child_process *child, int exit_code, void *privdata);
|
||||
void *privdata;
|
||||
};
|
||||
|
||||
struct child_process * alloc_child_process(char *const argv[], const char *pwd);
|
||||
void free_child_process(struct child_process *child);
|
||||
|
||||
pid_t spawn_child(struct child_process *child, void (*exit_cb)(struct child_process *child, int exit_code, void *privdata), void *privdata);
|
||||
|
||||
void sigchld_handler(int sig);
|
||||
|
||||
#endif // _SPAWN_H_
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <getopt.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "configfile.h"
|
||||
#include "connection.h"
|
||||
@ -30,6 +31,7 @@
|
||||
#include "list.h"
|
||||
#include "logging.h"
|
||||
#include "sockaddr.h"
|
||||
#include "spawn.h"
|
||||
#include "tcpsocket.h"
|
||||
|
||||
#define DEFAULT_CONFIG "torrent-stats.conf"
|
||||
@ -111,12 +113,12 @@ int main(int argc, char *argv[])
|
||||
struct passwd *pwl;
|
||||
if (!(pwl = getpwnam(user))) {
|
||||
log_print(LOG_ERROR, "unknown user: %s", user);
|
||||
exit(-1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setgid(pwl->pw_gid) || setuid(pwl->pw_uid)) {
|
||||
log_print(LOG_ERROR, "setgid/setuid");
|
||||
exit(-1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,7 +131,7 @@ int main(int argc, char *argv[])
|
||||
if (!debug) {
|
||||
/* start logging */
|
||||
if (log_init(logfile) < 0)
|
||||
exit(-1);
|
||||
exit(1);
|
||||
|
||||
/* zum daemon mutieren */
|
||||
daemon(-1, 0);
|
||||
@ -149,6 +151,14 @@ int main(int argc, char *argv[])
|
||||
struct timeval tv = { .tv_sec = interval, .tv_usec = 0 };
|
||||
event_add_timeout(&tv, ctcs_trigger_status, (void *)timeout);
|
||||
|
||||
|
||||
struct sigaction sigchld_action = {
|
||||
.sa_handler = sigchld_handler,
|
||||
.sa_flags = SA_NOCLDSTOP,
|
||||
};
|
||||
sigaction(SIGCHLD, &sigchld_action, NULL);
|
||||
|
||||
|
||||
event_loop();
|
||||
return 0;
|
||||
}
|
||||
|
@ -5,4 +5,7 @@ listen-http 0.0.0.0:8080
|
||||
status-interval 10
|
||||
seed-timeout 300
|
||||
|
||||
ctorrent-bin /usr/bin/ctorrent
|
||||
search-path /home/upload
|
||||
|
||||
logfile torrent-stats.log
|
||||
|
Loading…
Reference in New Issue
Block a user