autostart ctorrent

This commit is contained in:
Olaf Rempel 2009-02-25 14:17:23 +01:00
parent 8f0d6c1f54
commit 6301d8a640
7 changed files with 337 additions and 8 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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
View 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
View 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_

View File

@ -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;
}

View File

@ -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