Add monitor, httpserver, controller, tests and tools.

This commit is contained in:
Jiaqiang Xu 2012-08-07 10:40:34 +08:00
parent 1671133d5e
commit 449772e147
85 changed files with 8287 additions and 32 deletions

27
.gitignore vendored
View File

@ -37,6 +37,9 @@ m4/lt~obsolete.m4
ccnet-*.tar.gz
config.h.in
py-compile
intltool-extract.in
intltool-merge.in
intltool-update.in
*.stamp
*.pyc
*.tmp.ui
@ -67,5 +70,29 @@ app/seafserv-tool
daemon/seaf-daemon
gui/gtk/seafile-applet
server/seaf-server
monitor/seaf-mon
controller/seafile-controller
httpserver/httpserver
tests/common-conf.sh
tools/seaf-server-init
*.mo
ccnet-web.sh
tests/basic/conf1/c882e263e9d02c63ca6b61c68508761cbc74c358.peer
tests/basic/conf1/c882e263e9d02c63ca6b61c68508761cbc74c358.user
tests/basic/conf1/group-db/
tests/basic/conf1/peer-db/
tests/basic/conf1/user-db/
tests/basic/conf2/376cf9b6ef33a6839cf1fc096131893b5ecc673f.peer
tests/basic/conf2/376cf9b6ef33a6839cf1fc096131893b5ecc673f.user
tests/basic/conf2/group-db/
tests/basic/conf2/peer-db/
tests/basic/conf2/user-db/
tests/basic/conf3/1e5b5e0f49010b94aa6c2995a6e7b7cba462d388.peer
tests/basic/conf3/1e5b5e0f49010b94aa6c2995a6e7b7cba462d388.user
tests/basic/conf3/group-db/
tests/basic/conf3/peer-db/
tests/basic/conf3/user-db/
tests/basic/conf4/93ae3e01eea6667cbdd03c4afde413ccd9f1eb43.peer
tests/basic/conf4/93ae3e01eea6667cbdd03c4afde413ccd9f1eb43.user
tests/basic/conf4/peer-db/
tests/basic/conf4/user-db/

View File

@ -3,22 +3,20 @@ if COMPILE_GUI
MAKE_GUI = gui
endif
#if COMPILE_HTTPSERVER
# MAKE_HTTPSERVER = httpserver
#endif
if COMPILE_HTTPSERVER
MAKE_HTTPSERVER = httpserver
endif
if COMPILE_SERVER
# MAKE_SERVER = server tools monitor $(MAKE_HTTPSERVER) controller
MAKE_SERVER = server
MAKE_SERVER = server tools monitor $(MAKE_HTTPSERVER) controller
endif
if COMPILE_CLIENT
MAKE_CLIENT = daemon
endif
#SUBDIRS = include lib common daemon $(MAKE_CLINET) $(MAKE_SERVER) \
# app python tests
SUBDIRS = include lib common daemon $(MAKE_GUI) $(MAKE_SERVER) app python web
SUBDIRS = include lib common daemon $(MAKE_CLINET) $(MAKE_SERVER) \
app python tests
INTLTOOL = \
intltool-extract.in \
@ -58,4 +56,4 @@ endif
uninstall-hook:
chmod u+rw -R $(DESTDIR)${pkglibdir}/web
rm -rf $(DESTDIR)${pkglibdir}/web
rm -rf $(DESTDIR)${pkglibdir}/web

View File

@ -103,7 +103,7 @@ if test "$bwin32" = true; then
compile_cli=no
compile_tools=no
compile_server=no
# compile_httpserver=no
compile_httpserver=no
fi
if test "$bmac" = true; then
@ -112,13 +112,13 @@ if test "$bmac" = true; then
compile_cli=no
compile_tools=no
compile_server=no
# compile_httpserver=no
compile_httpserver=no
AC_ARG_ENABLE(server, AC_HELP_STRING([--enable-server], [enable server]),
[compile_server=$enableval],[compile_server="no"])
# AC_ARG_ENABLE(httpserver, AC_HELP_STRING([--enable-httpserver], [enable httpserver]),
# [compile_httpserver=$enableval],[compile_httpserver="no"])
AC_ARG_ENABLE(httpserver, AC_HELP_STRING([--enable-httpserver], [enable httpserver]),
[compile_httpserver=$enableval],[compile_httpserver="no"])
fi
@ -128,7 +128,7 @@ if test "$blinux" = true; then
compile_tools=yes
compile_server=no
# compile_httpserver=no
compile_httpserver=no
AC_ARG_ENABLE(client, AC_HELP_STRING([--enable-client], [enable server]),
@ -143,17 +143,13 @@ if test "$blinux" = true; then
[compile_server=$enableval],[compile_server="no"])
# AC_ARG_ENABLE(httpserver, AC_HELP_STRING([--enable-httpserver], [enable httpserver]),
# [compile_httpserver=$enableval],[compile_httpserver="no"])
AC_ARG_ENABLE(httpserver, AC_HELP_STRING([--enable-httpserver], [enable httpserver]),
[compile_httpserver=$enableval],[compile_httpserver="no"])
# AC_ARG_ENABLE(seablock, AC_HELP_STRING([--enable-seablock],
# [enable seablock]), [compile_seablock=$enableval],
# [compile_seablock="no"])
# AC_ARG_ENABLE(cluster, AC_HELP_STRING([--enable-cluster],
# [enable cluster]), [compile_cluster=$enableval],
# [compile_cluster="no"])
AC_ARG_ENABLE(riak, AC_HELP_STRING([--enable-riak], [enable riak backend]),
[compile_riak=$enableval],[compile_riak="no"])
@ -194,7 +190,7 @@ if test x${server_pkg} = xyes; then
compile_cli=yes
compile_tools=yes
compile_server=yes
# compile_httpserver=yes
compile_httpserver=yes
SERVER_PKG_RPATH=-Wl,-R,\'\$\$ORIGIN/../lib\'
SERVER_PKG_PY_RPATH=-Wl,-R,\'\$\$ORIGIN/../../..\'
fi
@ -207,9 +203,8 @@ AM_CONDITIONAL([COMPILE_TOOLS], [test "${compile_tools}" = "yes"])
AM_CONDITIONAL([COMPILE_PYTHON], [test "${compile_python}" = "yes"])
AM_CONDITIONAL([COMPILE_CLIENT], [test "${compile_client}" = "yes"])
AM_CONDITIONAL([COMPILE_SERVER], [test "${compile_server}" = "yes"])
#AM_CONDITIONAL([COMPILE_HTTPSERVER], [test "${compile_httpserver}" = "yes"])
AM_CONDITIONAL([COMPILE_HTTPSERVER], [test "${compile_httpserver}" = "yes"])
#AM_CONDITIONAL([COMPILE_SEABLOCK], [test "${compile_seablock}" = "yes"])
#AM_CONDITIONAL([COMPILE_CLUSTER], [test "${compile_cluster}" = "yes"])
AM_CONDITIONAL([COMPILE_RIAK], [test "${compile_riak}" = "yes"])
AM_CONDITIONAL([COMPILE_CEPH], [test "${compile_ceph}" = "yes"])
@ -461,13 +456,13 @@ AC_CONFIG_FILES(
app/Makefile
python/Makefile
python/seafile/Makefile
controller/Makefile
httpserver/Makefile
monitor/Makefile
tools/Makefile
tests/Makefile
tests/common-conf.sh
)
#controller/Makefile
#httpserver/Makefile
#monitor/Makefile
#tools/Makefile
#tests/Makefile
#tests/common-conf.sh
AC_OUTPUT
@ -485,9 +480,9 @@ if test x${compile_server} = xyes; then
echo "seaf-server"
fi
#if test x${compile_httpserver} = xyes; then
# echo "httpserver"
#fi
if test x${compile_httpserver} = xyes; then
echo "httpserver"
fi
#if test x${compile_seablock} = xyes; then
# echo "seablock"
#fi

22
controller/Makefile.am Normal file
View File

@ -0,0 +1,22 @@
bin_PROGRAMS = seafile-controller
AM_CFLAGS = \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/common \
@CCNET_CFLAGS@ \
@SEARPC_CFLAGS@ \
@GLIB2_CFLAGS@ \
@MYSQL_CFLAGS@ \
@ZDB_CFLAGS@ \
-Wall
noinst_HEADERS = seafile-controller.h ../common/log.h
seafile_controller_SOURCES = seafile-controller.c ../common/log.c
seafile_controller_LDADD = @CCNET_LIBS@ \
@GLIB2_LIBS@ @GOBJECT_LIBS@ -lssl @LIB_RT@ @LIB_UUID@ -levent \
@SEARPC_LIBS@
seafile_controller_LDFLAGS = @STATIC_COMPILE@ @SERVER_PKG_RPATH@

View File

@ -0,0 +1,657 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ccnet.h>
#include <glib.h>
#include "utils.h"
#include <getopt.h>
#include "log.h"
#include "seafile-controller.h"
#define CHECK_HEARTBEAT_INTERVAL 2 /* every 2 seconds */
#define MAX_HEARTBEAT_LIMIT 4
SeafileController *ctl;
static const char *short_opts = "hvfb:c:d:r:l:g:G:";
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h', },
{ "version", no_argument, NULL, 'v', },
{ "foreground", no_argument, NULL, 'f', },
{ "bin-dir", required_argument, NULL, 'b', },
{ "config-dir", required_argument, NULL, 'c', },
{ "seafile-dir", required_argument, NULL, 'd', },
{ "logfile", required_argument, NULL, 'l', },
{ "ccnet-debug-level", required_argument, NULL, 'g' },
{ "seafile-debug-level", required_argument, NULL, 'G' },
};
static void controller_exit (int code) __attribute__((noreturn));
static void
controller_exit (int code)
{
if (code != 0) {
seaf_warning ("seaf-controller exited with code %d\n", code);
}
exit(code);
}
/* returns the pid of the newly created process */
static int
spawn_process (char *argv[])
{
pid_t pid = fork();
char **ptr = argv;
GString *buf = g_string_new(argv[0]);
while (*(++ptr)) {
g_string_append_printf (buf, " %s", *ptr);
}
seaf_message ("spawn_process: %s\n", buf->str);
g_string_free (buf, TRUE);
if (pid == 0) {
/* child process */
execvp (argv[0], argv);
seaf_warning ("failed to execvp %s\n", argv[0]);
exit(-1);
} else {
/* controller */
if (pid == -1)
seaf_warning ("error when fork %s: %s\n", argv[0], strerror(errno));
else
seaf_message ("spawned %s, pid %d\n", argv[0], pid);
return (int)pid;
}
}
/* If --bin-dir is specified, modify the <PATH> env before spawning any
* process. */
static void
set_path_env (const char *bin_dir)
{
if (!bin_dir)
return;
const char *path = g_getenv("PATH");
if (!path) {
g_setenv ("PATH", bin_dir, TRUE);
} else {
GString *buf = g_string_new (NULL);
g_string_append_printf (buf, "%s:%s", bin_dir, path);
g_setenv ("PATH", buf->str, TRUE);
g_string_free (buf, TRUE);
}
}
static int
start_ccnet_server ()
{
if (!ctl->config_dir)
return -1;
seaf_message ("starting ccnet-server ...\n");
char *argv[] = {
"ccnet-server",
"-c", ctl->config_dir,
"-d",
"-P", ctl->pidfile[PID_CCNET],
NULL};
int pid = spawn_process (argv);
if (pid <= 0) {
seaf_warning ("Failed to spawn ccnet-server\n");
return -1;
}
return 0;
}
static int
start_seaf_server ()
{
if (!ctl->config_dir || !ctl->seafile_dir)
return -1;
seaf_message ("starting seaf-server ...\n");
char *argv[] = {
"seaf-server",
"-c", ctl->config_dir,
"-d", ctl->seafile_dir,
"-P", ctl->pidfile[PID_SERVER],
NULL};
int pid = spawn_process (argv);
if (pid <= 0) {
seaf_warning ("Failed to spawn seaf-server\n");
return -1;
}
return 0;
}
static int
start_seaf_monitor ()
{
if (!ctl->config_dir || !ctl->seafile_dir)
return -1;
seaf_message ("starting seaf-mon ...\n");
char *argv[] = {
"seaf-mon",
"-c", ctl->config_dir,
"-d", ctl->seafile_dir,
"-P", ctl->pidfile[PID_MONITOR],
NULL};
int pid = spawn_process (argv);
if (pid <= 0) {
seaf_warning ("Failed to spawn seaf-mon\n");
return -1;
}
return 0;
}
#define IS_APP_MSG(msg,topic) (strcmp((msg)->app, topic) == 0)
static void mq_cb (CcnetMessage *msg, void *data)
{
time_t now = time (NULL);
if (IS_APP_MSG(msg, "seaf_server.heartbeat")) {
ctl->last_hb[HB_SEAFILE_SERVER] = now;
} else if (IS_APP_MSG(msg, "seaf_mon.heartbeat")) {
ctl->last_hb[HB_SEAFILE_MONITOR] = now;
}
}
static int
start_mq_client ()
{
seaf_message ("starting mq client ...\n");
CcnetMqclientProc *mqclient_proc;
mqclient_proc = (CcnetMqclientProc *)
ccnet_proc_factory_create_master_processor
(ctl->client->proc_factory, "mq-client");
if (!mqclient_proc) {
seaf_warning ("Failed to create mqclient proc.\n");
return -1;
}
static char *topics[] = {
"seaf_server.heartbeat",
"seaf_mon.heartbeat",
};
ccnet_mqclient_proc_set_message_got_cb (mqclient_proc, mq_cb, NULL);
/* Subscribe to messages. */
if (ccnet_processor_start ((CcnetProcessor *)mqclient_proc,
G_N_ELEMENTS(topics), topics) < 0) {
seaf_warning ("Failed to start mqclient proc\n");
return -1;
}
ctl->mqclient_proc = mqclient_proc;
return 0;
}
static void
stop_mq_client ()
{
if (ctl->mqclient_proc) {
seaf_message ("stopping mq client ...\n");
ccnet_mqclient_proc_unsubscribe_apps (ctl->mqclient_proc);
ctl->mqclient_proc = NULL;
}
}
static void
run_controller_loop ()
{
GMainLoop *mainloop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (mainloop);
}
static int
read_pid_from_pidfile (const char *pidfile)
{
FILE *pf = fopen (pidfile, "r");
if (!pf) {
seaf_warning ("failed to open pidfile %s:%s\n",
pidfile, strerror(errno));
return -1;
}
int pid = -1;
if (fscanf (pf, "%d", &pid) < 0) {
seaf_warning ("bad pidfile format: %s\n", pidfile);
return -1;
}
return pid;
}
static void
try_kill_process(int which)
{
if (which < 0 || which >= N_PID)
return;
char *pidfile = ctl->pidfile[which];
int pid = read_pid_from_pidfile(pidfile);
if (pid > 0)
kill((pid_t)pid, SIGTERM);
}
static gboolean
check_heartbeat (void *data)
{
time_t now = time(NULL);
int i;
for (i = 0; i < N_HEARTBEAT; i++) {
if (ctl->last_hb[i] == 0)
ctl->last_hb[i] = now;
}
if (now - ctl->last_hb[HB_SEAFILE_SERVER] > MAX_HEARTBEAT_LIMIT) {
try_kill_process(PID_SERVER);
seaf_message ("seaf-server need restart...\n");
start_seaf_server ();
ctl->last_hb[HB_SEAFILE_SERVER] = now;
}
if (now - ctl->last_hb[HB_SEAFILE_MONITOR] > MAX_HEARTBEAT_LIMIT) {
try_kill_process(PID_MONITOR);
seaf_message ("seaf-mon need restart...\n");
start_seaf_monitor ();
ctl->last_hb[HB_SEAFILE_MONITOR] = now;
}
return TRUE;
}
static void
start_hearbeat_monitor ()
{
ctl->hearbeat_timer = g_timeout_add (
CHECK_HEARTBEAT_INTERVAL * 1000, check_heartbeat, NULL);
}
static void
stop_heartbeat_monitor ()
{
if (ctl->hearbeat_timer != 0) {
g_source_remove (ctl->hearbeat_timer);
ctl->hearbeat_timer = 0;
ctl->last_hb[HB_SEAFILE_SERVER] = 0;
ctl->last_hb[HB_SEAFILE_MONITOR] = 0;
}
}
static void
disconnect_clients ()
{
CcnetClient *client, *sync_client;
client = ctl->client;
sync_client = ctl->sync_client;
if (client->connected) {
ccnet_client_disconnect_daemon (client);
}
if (sync_client->connected) {
ccnet_client_disconnect_daemon (sync_client);
}
}
static void rm_client_fd_from_mainloop ();
static int seaf_controller_start ();
static void
on_ccnet_daemon_down ()
{
stop_heartbeat_monitor ();
stop_mq_client ();
disconnect_clients ();
rm_client_fd_from_mainloop ();
seaf_message ("restarting ccnet server ...\n");
/* restart ccnet */
if (seaf_controller_start () < 0) {
seaf_warning ("Failed to restart ccnet server.\n");
controller_exit (1);
}
}
static gboolean
client_io_cb (GIOChannel *source, GIOCondition condition, gpointer data)
{
if (condition & G_IO_IN) {
if (ccnet_client_read_input (ctl->client) <= 0) {
on_ccnet_daemon_down ();
return FALSE;
}
return TRUE;
} else {
on_ccnet_daemon_down ();
return FALSE;
}
}
static void
add_client_fd_to_mainloop ()
{
GIOChannel *channel;
channel = g_io_channel_unix_new (ctl->client->connfd);
ctl->client_io_id = g_io_add_watch (channel,
G_IO_IN | G_IO_HUP | G_IO_ERR,
client_io_cb, NULL);
}
static void
rm_client_fd_from_mainloop ()
{
if (ctl->client_io_id != 0) {
g_source_remove (ctl->client_io_id);
ctl->client_io_id = 0;
}
}
static void
on_ccnet_connected ()
{
if (start_seaf_server () < 0)
controller_exit(1);
if (start_seaf_monitor () < 0)
controller_exit(1);
if (start_mq_client () < 0)
controller_exit(1);
add_client_fd_to_mainloop ();
start_hearbeat_monitor ();
}
static gboolean
do_connect_ccnet ()
{
CcnetClient *client, *sync_client;
client = ctl->client;
sync_client = ctl->sync_client;
if (!client->connected) {
if (ccnet_client_connect_daemon (client, CCNET_CLIENT_ASYNC) < 0) {
return TRUE;
}
}
if (!sync_client->connected) {
if (ccnet_client_connect_daemon (sync_client, CCNET_CLIENT_SYNC) < 0) {
return TRUE;
}
}
seaf_message ("ccnet daemon connected.\n");
on_ccnet_connected ();
return FALSE;
}
/* This would also stop seaf-server & seaf-mon */
static void
stop_ccnet_server ()
{
seaf_message ("shutting down ccnet-server ...\n");
GError *error = NULL;
ccnet_client_send_cmd (ctl->sync_client, "shutdown", &error);
try_kill_process(PID_CCNET);
try_kill_process(PID_SERVER);
try_kill_process(PID_MONITOR);
}
static void
init_pidfile_path (SeafileController *ctl)
{
char tmp[] = "XXXXXX";
char buf[PATH_MAX];
int pid = (int)getpid();
if (!mktemp(tmp))
return;
/* use controller pid and mktemp to generate unique path */
snprintf (buf, sizeof(buf), "/tmp/seafile-%d-%s.ccnet.pid", pid, tmp);
ctl->pidfile[PID_CCNET] = g_strdup(buf);
snprintf (buf, sizeof(buf), "/tmp/seafile-%d-%s.server.pid", pid, tmp);
ctl->pidfile[PID_SERVER] = g_strdup(buf);
snprintf (buf, sizeof(buf), "/tmp/seafile-%d-%s.monitor.pid", pid, tmp);
ctl->pidfile[PID_MONITOR] = g_strdup(buf);
}
static int
seaf_controller_init (SeafileController *ctl, char *bin_dir,
char *config_dir, char *seafile_dir)
{
if (bin_dir) {
if (!g_file_test (bin_dir, G_FILE_TEST_IS_DIR)) {
seaf_warning ("invalid config_dir: %s\n", config_dir);
return -1;
}
}
if (!g_file_test (config_dir, G_FILE_TEST_IS_DIR)) {
seaf_warning ("invalid config_dir: %s\n", config_dir);
return -1;
}
if (!g_file_test (seafile_dir, G_FILE_TEST_IS_DIR)) {
seaf_warning ("invalid seafile_dir: %s\n", seafile_dir);
return -1;
}
ctl->client = ccnet_client_new ();
ctl->sync_client = ccnet_client_new ();
if (ccnet_client_load_confdir (ctl->client, config_dir) < 0) {
seaf_warning ("Failed to load ccnet confdir\n");
return -1;
}
if (ccnet_client_load_confdir (ctl->sync_client, config_dir) < 0) {
seaf_warning ("Failed to load ccnet confdir\n");
return -1;
}
ctl->config_dir = config_dir;
ctl->bin_dir = bin_dir;
ctl->seafile_dir = seafile_dir;
init_pidfile_path(ctl);
return 0;
}
static int
seaf_controller_start ()
{
if (start_ccnet_server () < 0) {
seaf_warning ("Failed to start ccnet server\n");
return -1;
}
g_timeout_add (1000 * 1, do_connect_ccnet, NULL);
return 0;
}
static void
sigint_handler (int signo)
{
stop_ccnet_server ();
signal (signo, SIG_DFL);
raise (signo);
}
static void
sigchld_handler (int signo)
{
waitpid (-1, NULL, WNOHANG);
}
static void
set_signal_handlers ()
{
signal (SIGINT, sigint_handler);
signal (SIGTERM, sigint_handler);
signal (SIGCHLD, sigchld_handler);
signal (SIGPIPE, SIG_IGN);
}
static void
usage ()
{
fprintf (stderr, "Usage: seafile-controller OPTIONS\n"
"OPTIONS:\n"
" -b, --bin-dir insert a directory in front of the PATH env\n"
" -c, --config-dir ccnet config dir\n"
" -d, --seafile-dir seafile dir\n"
);
}
int main (int argc, char **argv)
{
if (argc <= 1) {
usage ();
exit (1);
}
char *bin_dir = NULL;
char *config_dir = DEFAULT_CONFIG_DIR;
char *seafile_dir = NULL;
char *logfile = NULL;
char *ccnet_debug_level_str = "info";
char *seafile_debug_level_str = "debug";
int daemon_mode = 1;
int c;
while ((c = getopt_long (argc, argv, short_opts,
long_opts, NULL)) != EOF)
{
switch (c) {
case 'h':
usage ();
exit(1);
break;
case 'v':
fprintf (stderr, "seafile-controller version 1.0\n");
break;
case 'b':
bin_dir = optarg;
break;
case 'c':
config_dir = optarg;
break;
case 'd':
seafile_dir = g_strdup(optarg);
break;
case 'f':
daemon_mode = 0;
break;
case 'l':
logfile = g_strdup(optarg);
break;
case 'g':
ccnet_debug_level_str = optarg;
break;
case 'G':
seafile_debug_level_str = optarg;
break;
default:
usage ();
exit (1);
}
}
if (daemon_mode)
daemon (1, 0);
g_type_init ();
#if !GLIB_CHECK_VERSION(2,32,0)
g_thread_init (NULL);
#endif
if (!seafile_dir) {
seaf_warning ("<seafile_dir> must be specified with --seafile-dir\n");
controller_exit(1);
}
if (bin_dir)
bin_dir = ccnet_expand_path(bin_dir);
config_dir = ccnet_expand_path (config_dir);
seafile_dir = ccnet_expand_path(seafile_dir);
ctl = g_new0 (SeafileController, 1);
if (seaf_controller_init (ctl, bin_dir, config_dir, seafile_dir) < 0) {
controller_exit(1);
}
if (!logfile) {
logfile = g_build_filename (seafile_dir, "controller.log", NULL);
}
if (seafile_log_init (logfile, ccnet_debug_level_str,
seafile_debug_level_str) < 0) {
seaf_warning ("Failed to init log.\n");
controller_exit (1);
}
set_signal_handlers ();
if (ctl->bin_dir)
set_path_env (ctl->bin_dir);
if (seaf_controller_start (ctl) < 0)
controller_exit (1);
run_controller_loop ();
return 0;
}

View File

@ -0,0 +1,56 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Seafile-controller is responsible for:
*
* 1. Start: start server processes:
*
* - ccnet-server
* - seaf-server
* - seaf-mon
*
* 2. Repair:
*
* - ensure ccnet process availability by watching client->connfd
* - ensure server processes availablity by receiving heartbeat
* messages.
* If heartbeat messages for some process is not received for a given
* time, try to restart it.
*
*/
#ifndef SEAFILE_CONTROLLER_H
#define SEAFILE_CONTROLLER_H
typedef struct _SeafileController SeafileController;
enum {
HB_SEAFILE_SERVER = 0,
HB_SEAFILE_MONITOR,
N_HEARTBEAT
};
enum {
PID_CCNET = 0,
PID_SERVER,
PID_MONITOR,
N_PID
};
struct _SeafileController {
char *bin_dir;
char *config_dir;
char *seafile_dir;
CcnetClient *client;
CcnetClient *sync_client;
CcnetMqclientProc *mqclient_proc;
guint hearbeat_timer;
guint client_io_id;
time_t last_hb[N_HEARTBEAT];
int pid[N_PID];
char *pidfile[N_PID];
};
#endif

53
httpserver/Makefile.am Normal file
View File

@ -0,0 +1,53 @@
AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \
-DSEAFILE_SERVER \
-DHTTP_SERVER \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/common \
-I$(includedir) \
@CCNET_CFLAGS@ \
@SEARPC_CFLAGS@ \
@GLIB2_CFLAGS@ \
@MYSQL_CFLAGS@ \
@ZDB_CFLAGS@ \
@CURL_CFLAGS@ \
-Wall
bin_PROGRAMS = httpserver
noinst_HEADERS = seafile-session.h repo-mgr.h
httpserver_SOURCES = \
httpserver.c \
seafile-session.c \
repo-mgr.c \
../common/seaf-db.c \
../common/bitfield.c \
../common/branch-mgr.c \
../common/fs-mgr.c \
../common/block-mgr.c \
../common/block-backend.c \
../common/block-backend-fs.c \
../common/block-backend-ceph.c \
../common/commit-mgr.c \
../common/log.c \
../common/avl/avl.c \
../common/object-list.c \
../common/seaf-utils.c \
../common/obj-store.c \
../common/obj-backend-fs.c \
../common/obj-backend-riak.c \
../common/riak-http-client.c \
../common/seafile-crypt.c
# XXX: -levent_openssl must be behind in -levhtp
httpserver_LDADD = -levent -levhtp -lssl -levent_openssl \
@GLIB2_LIBS@ @GOBJECT_LIBS@ @LIB_RT@ \
@CCNET_LIBS@ \
$(top_builddir)/lib/libseafile.la \
$(top_builddir)/common/cdc/libcdc.la \
@MYSQL_LIBS@ @SEARPC_LIBS@ @ZDB_LIBS@ @RADOS_LIBS@ @CURL_LIBS@
httpserver_LDFLAGS = @STATIC_COMPILE@

7
httpserver/README Normal file
View File

@ -0,0 +1,7 @@
== Compile ==
1. compile and install libevhtp library first
cd libevhtp
cmake . # you need to install cmake program to compile it
make
make install

View File

@ -0,0 +1,6 @@
txt text/plain
html text/html
doc application/word
docx application/word
mp3 audio/mp3
pdf application/pdf

View File

@ -0,0 +1,8 @@
<html>
<head>
<title>Seafile Httpserver</title>
</head>
<body>
<p>Welcome to seafile httpserver</p>
</body>
</html>

View File

@ -0,0 +1,24 @@
<html>
<head>
<title>Seafile Httpserver Upload File</title>
</head>
<body>
<form enctype="multipart/form-data" action="http://127.0.0.1:8082/putfile/" method="post">
<p>
Please input a repo id that the file will be put:
<input type="text" name="repoid">
</p>
<p>
Please input a path that the file will be stored:
<input type="text" name="uploadpath">
</p>
<p>
Please select a file that will be uploaded:
<input type="file" name="uploadfile">
</p>
<p>
<input type="submit" value="Upload">
</p>
</form>
</body>
</html>

639
httpserver/httpserver.c Normal file
View File

@ -0,0 +1,639 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>
#include <glib.h>
#include <event.h>
#include <evhtp.h>
#include <searpc.h>
#include <ccnet.h>
#include <searpc-client.h>
#include <ccnetrpc-transport.h>
#include <seafile-session.h>
#include <seafile-object.h>
#include <seafile-crypt.h>
#include "seafile.h"
#include "utils.h"
#include "httpserver.h"
/* for debugging */
/*#define DEBUG*/
typedef struct HttpData {
SearpcClient *rpc_client;
SearpcClient *threaded_rpc_client;
} HttpData;
static char *config_dir = NULL;
static char *seafile_dir = NULL;
static char *bind_addr = "0.0.0.0";
static uint16_t bind_port = 8082;
static int num_threads = 10;
static char *root_dir = NULL;
CcnetClient *ccnet_client;
SeafileSession *seaf;
static const char *short_opts = "hvfc:d:p:b:t:r:";
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h', },
{ "version", no_argument, NULL, 'v', },
{ "foreground", no_argument, NULL, 'f', },
{ "config-file", required_argument, NULL, 'c', },
{ "seafdir", required_argument, NULL, 'd', },
{ "port", required_argument, NULL, 'p', },
{ "bindaddr", required_argument, NULL, 'b', },
{ "threads", required_argument, NULL, 't', },
{ "root", required_argument, NULL, 'r', },
};
static void usage ()
{
fprintf (stderr, "usage: httpserver [-c config_dir] [-d seafile_dir] -r http_root_dir\n");
}
#define CONTENT_TYPE_FILENAME "content-type.txt"
#define FILE_TYPE_MAP_DEFAULT_LEN 1
struct file_type_map *ftmap;
static int
load_content_type_map(struct file_type_map **ftmap)
{
FILE *fin;
int cnt = 0;
int max = FILE_TYPE_MAP_DEFAULT_LEN;
char suffix[16], type[16];
char path[PATH_MAX];
*ftmap = (struct file_type_map *)malloc(sizeof(struct file_type_map) *
FILE_TYPE_MAP_DEFAULT_LEN);
if (*ftmap == NULL)
return -1;
snprintf(path, PATH_MAX, "%s/%s", root_dir, CONTENT_TYPE_FILENAME);
fin = fopen(path, "r");
if (fin == NULL) {
g_warning ("cann't open content type file\n");
return -1;
}
while (!feof(fin)) {
fscanf(fin, "%s %s\n", suffix, type);
(*ftmap)[cnt].suffix = strdup(suffix);
(*ftmap)[cnt].type = strdup(type);
cnt++;
/* realloc ftmap */
if (cnt >= max) {
max *= 2;
*ftmap = realloc(*ftmap, sizeof(struct file_type_map) * max);
if (*ftmap == NULL)
return -1;
}
}
(*ftmap)[cnt].suffix = NULL;
(*ftmap)[cnt].type = NULL;
fclose(fin);
#ifdef DEBUG
{
struct file_type_map *p;
g_warning ("%d %d\n", cnt, max);
for (p = *ftmap; p->suffix != NULL; p++) {
g_warning ("%s %s\n", p->suffix, p->type);
}
}
#endif
return 0;
}
static void
default_cb(evhtp_request_t *req, void *arg)
{
/* Return empty page. */
evhtp_send_reply (req, EVHTP_RES_OK);
}
static char *
parse_content_type(const char *filename)
{
char *p;
int i;
if ((p = strrchr(filename, '.')) == NULL)
return NULL;
p++;
for (i = 0; ftmap[i].suffix != NULL; i++) {
if (strcmp(p, ftmap[i].suffix) == 0)
return ftmap[i].type;
}
return NULL;
}
static gboolean
test_firefox (evhtp_request_t *req)
{
const char *user_agent = evhtp_header_find (req->headers_in, "User-Agent");
if (!user_agent)
return FALSE;
GString *s = g_string_new (user_agent);
if (g_strrstr (g_string_ascii_down (s)->str, "firefox")) {
g_string_free (s, TRUE);
return TRUE;
}
else {
g_string_free (s, TRUE);
return FALSE;
}
}
typedef struct SendfileData {
evhtp_request_t *req;
Seafile *file;
SeafileCrypt *crypt;
EVP_CIPHER_CTX ctx;
BlockHandle *handle;
size_t remain;
int idx;
bufferevent_data_cb saved_read_cb;
bufferevent_data_cb saved_write_cb;
bufferevent_event_cb saved_event_cb;
void *saved_cb_arg;
} SendfileData;
static void
free_sendfile_data (SendfileData *data)
{
seafile_unref (data->file);
g_free (data->crypt);
g_free (data);
}
static void
write_data_cb (struct bufferevent *bev, void *ctx)
{
SendfileData *data = ctx;
char *blk_id;
BlockHandle *handle;
char buf[1024 * 64];
int n;
next:
blk_id = data->file->blk_sha1s[data->idx];
if (!data->handle) {
data->handle = seaf_block_manager_open_block(seaf->block_mgr,
blk_id, BLOCK_READ);
if (!data->handle) {
g_warning ("Failed to open block %s\n", blk_id);
goto err;
}
BlockMetadata *bmd;
bmd = seaf_block_manager_stat_block_by_handle (seaf->block_mgr,
data->handle);
data->remain = bmd->size;
g_free (bmd);
if (data->crypt) {
if (seafile_decrypt_init (&data->ctx, data->crypt) < 0) {
g_warning ("Failed to init decrypt.\n");
goto err;
}
}
}
handle = data->handle;
n = seaf_block_manager_read_block(seaf->block_mgr, handle, buf, sizeof(buf));
data->remain -= n;
if (n < 0) {
g_warning ("Error when reading from block %s.\n", blk_id);
seaf_block_manager_close_block(seaf->block_mgr, handle);
seaf_block_manager_block_handle_free(seaf->block_mgr, handle);
goto err;
} else if (n == 0) {
seaf_block_manager_close_block (seaf->block_mgr, handle);
seaf_block_manager_block_handle_free (seaf->block_mgr, handle);
/* We've read up the data of this block, finish or try next block. */
if (data->idx == data->file->n_blocks - 1) {
/* Recover evhtp's callbacks */
struct bufferevent *bev = evhtp_request_get_bev (data->req);
bev->readcb = data->saved_read_cb;
bev->writecb = data->saved_write_cb;
bev->errorcb = data->saved_event_cb;
bev->cbarg = data->saved_cb_arg;
/* Resume reading incomming requests. */
evhtp_request_resume (data->req);
evhtp_send_reply_end (data->req);
free_sendfile_data (data);
return;
}
++(data->idx);
data->handle = NULL;
goto next;
}
/* OK, we've got some data to send. */
if (data->crypt != NULL) {
char *dec_out;
int dec_out_len = -1;
dec_out = g_new (char, n + 16);
if (!dec_out) {
g_warning ("Failed to alloc memory.\n");
goto err;
}
int ret = seafile_decrypt_update (&data->ctx,
dec_out,
&dec_out_len,
buf,
n);
if (ret != 0) {
g_warning ("Decrypt block %s failed.\n", blk_id);
g_free (dec_out);
goto err;
}
bufferevent_write (bev, dec_out, dec_out_len);
/* If it's the last piece of a block, call decrypt_final()
* to decrypt the possible partial block. */
if (data->remain == 0) {
ret = seafile_decrypt_final (&data->ctx, dec_out, &dec_out_len);
if (ret != 0) {
g_warning ("Decrypt block %s failed.\n", blk_id);
g_free (dec_out);
goto err;
}
bufferevent_write (bev, dec_out, dec_out_len);
}
g_free (dec_out);
} else {
bufferevent_write (bev, buf, n);
}
return;
err:
evhtp_connection_free (evhtp_request_get_connection (data->req));
free_sendfile_data (data);
return;
}
static void
my_event_cb (struct bufferevent *bev, short events, void *ctx)
{
SendfileData *data = ctx;
data->saved_event_cb (bev, events, data->saved_cb_arg);
/* Free aux data. */
free_sendfile_data (data);
}
static int
do_file(evhtp_request_t *req, SeafRepo *repo, const char *file_id,
const char *filename, const char *operation,
SeafileCryptKey *crypt_key)
{
Seafile *file;
char *type = NULL;
char file_size[255];
gchar *content_type = NULL;
char cont_filename[PATH_MAX];
char *key_hex, *iv_hex;
unsigned char enc_key[16], enc_iv[16];
SeafileCrypt *crypt = NULL;
SendfileData *data;
file = seaf_fs_manager_get_seafile(seaf->fs_mgr, file_id);
if (file == NULL)
return -1;
if (crypt_key != NULL) {
g_object_get (crypt_key,
"key", &key_hex,
"iv", &iv_hex,
NULL);
hex_to_rawdata (key_hex, enc_key, 16);
hex_to_rawdata (iv_hex, enc_iv, 16);
crypt = seafile_crypt_new (repo->enc_version, enc_key, enc_iv);
g_free (key_hex);
g_free (iv_hex);
}
evhtp_headers_add_header(req->headers_out,
evhtp_header_new("Access-Control-Allow-Origin",
"*", 0, 0));
type = parse_content_type(filename);
if (type != NULL) {
if (strstr(type, "text")) {
content_type = g_strjoin("; ", type, "charset=gbk", NULL);
} else {
content_type = g_strdup (type);
}
evhtp_headers_add_header(req->headers_out,
evhtp_header_new("Content-Type",
content_type, 0, 0));
g_free (content_type);
}
snprintf(file_size, sizeof(file_size), "%"G_GINT64_FORMAT"", file->file_size);
evhtp_headers_add_header (req->headers_out,
evhtp_header_new("Content-Length", file_size, 0, 0));
if (strcmp(operation, "download") == 0) {
if (test_firefox (req)) {
snprintf(cont_filename, PATH_MAX,
"attachment;filename*=\"utf8\' \'%s\"", filename);
} else {
snprintf(cont_filename, PATH_MAX,
"attachment;filename=\"%s\"", filename);
}
} else {
if (test_firefox (req)) {
snprintf(cont_filename, PATH_MAX,
"inline;filename*=\"utf8\' \'%s\"", filename);
} else {
snprintf(cont_filename, PATH_MAX,
"inline;filename=\"%s\"", filename);
}
}
evhtp_headers_add_header(req->headers_out,
evhtp_header_new("Content-Disposition", cont_filename,
0, 0));
/* If it's an empty file, send an empty reply. */
if (file->n_blocks == 0) {
evhtp_send_reply (req, EVHTP_RES_OK);
seafile_unref (file);
return 0;
}
data = g_new0 (SendfileData, 1);
data->req = req;
data->file = file;
data->crypt = crypt;
/* We need to overwrite evhtp's callback functions to
* write file data piece by piece.
*/
struct bufferevent *bev = evhtp_request_get_bev (req);
data->saved_read_cb = bev->readcb;
data->saved_write_cb = bev->writecb;
data->saved_event_cb = bev->errorcb;
data->saved_cb_arg = bev->cbarg;
bufferevent_setcb (bev,
NULL,
write_data_cb,
my_event_cb,
data);
/* Block any new request from this connection before finish
* handling this request.
*/
evhtp_request_pause (req);
/* Kick start data transfer by sending out http headers. */
evhtp_send_reply_start(req, EVHTP_RES_OK);
return 0;
}
static void
access_cb(evhtp_request_t *req, void *arg)
{
SeafRepo *repo = NULL;
char *error = NULL;
char *token = NULL;
char *filename = NULL;
const char *repo_id = NULL;
const char *id = NULL;
const char *operation = NULL;
const char *user = NULL;
CcnetClient *client;
SearpcClient *threaded_rpc_client, *rpc_client;
GError *err = NULL;
char *repo_role = NULL;
SeafileCryptKey *key = NULL;
SeafileWebAccess *webaccess = NULL;
token = req->uri->path->match_start;
*(token+strlen(token)-1) = '\0'; /* cut out last '/' in token */
token += 7; /* cut out '/files/' at head */
filename = req->uri->path->file;
client = ccnet_client_new ();
if ((ccnet_client_load_confdir(client, config_dir)) < 0 ) {
error = "Read config dir error\n";
goto bad_req_no_free;
}
if (ccnet_client_connect_daemon(client, CCNET_CLIENT_SYNC) < 0) {
error = "Connect to server failed\n";
goto bad_req;
}
rpc_client = ccnet_create_rpc_client (client, NULL,
"seafserv-rpcserver");
threaded_rpc_client = ccnet_create_rpc_client (
client, NULL, "seafserv-threaded-rpcserver");
webaccess = (SeafileWebAccess *) searpc_client_call__object (
rpc_client, "seafile_web_query_access_token", SEAFILE_TYPE_WEB_ACCESS,
NULL, 1, "string", token);
if (!webaccess) {
error = "Bad access token";
goto bad_req;
}
repo_id = seafile_web_access_get_repo_id (webaccess);
id = seafile_web_access_get_obj_id (webaccess);
operation = seafile_web_access_get_op (webaccess);
user = seafile_web_access_get_username (webaccess);
repo = seaf_repo_manager_get_repo(seaf->repo_mgr, repo_id);
if (!repo) {
error = "Bad repo id\n";
goto bad_req;
}
if (repo->encrypted) {
err = NULL;
key = (SeafileCryptKey *) seafile_get_decrypt_key (rpc_client,
repo_id, user, &err);
if (!key) {
error = "Repo is encrypted. Please provide password to view it.";
goto bad_req;
}
}
if (!seaf_fs_manager_object_exists (seaf->fs_mgr, id)) {
error = "Invalid file id\n";
goto bad_req;
}
if (do_file(req, repo, id, filename, operation, key) < 0) {
error = "Internal server error\n";
goto bad_req;
}
ccnet_rpc_client_free (rpc_client);
ccnet_rpc_client_free (threaded_rpc_client);
g_object_unref (client);
seaf_repo_unref (repo);
g_free (repo_role);
if (key != NULL)
g_object_unref (key);
g_object_unref (webaccess);
return;
bad_req:
g_free (rpc_client->arg);
searpc_client_free (rpc_client);
g_free (threaded_rpc_client->arg);
searpc_client_free (threaded_rpc_client);
g_object_unref (client);
if (repo != NULL)
seaf_repo_unref (repo);
g_free (repo_role);
if (key != NULL)
g_object_unref (key);
if (webaccess != NULL)
g_object_unref (webaccess);
bad_req_no_free:
g_warning ("fetch failed: %s\n", error);
evbuffer_add_printf(req->buffer_out, "%s\n", error);
evhtp_send_reply(req, EVHTP_RES_BADREQ);
}
int
main(int argc, char *argv[])
{
evbase_t *evbase = NULL;
evhtp_t *htp = NULL;
int daemon_mode = 1;
int c;
config_dir = DEFAULT_CONFIG_DIR;
while ((c = getopt_long(argc, argv,
short_opts, long_opts, NULL)) != EOF) {
switch (c) {
case 'h':
usage();
exit(0);
case 'v':
exit(-1);
break;
case 'c':
config_dir = strdup(optarg);
break;
case 'd':
seafile_dir = strdup(optarg);
break;
case 'p':
bind_port = atoi(optarg);
break;
case 'b':
bind_addr = strdup(optarg);
break;
case 't':
num_threads = atoi(optarg);
break;
case 'r':
root_dir = strdup(optarg);
break;
case 'f':
daemon_mode = 0;
break;
default:
usage();
exit(-1);
}
}
if (!root_dir) {
usage();
exit (-1);
}
#ifndef WIN32
if (daemon_mode)
daemon(1, 0);
#endif
g_type_init();
ccnet_client = ccnet_client_new();
if ((ccnet_client_load_confdir(ccnet_client, config_dir)) < 0) {
g_warning ("Read config dir error\n");
return -1;
}
if (seafile_dir == NULL)
seafile_dir = g_build_filename (config_dir, "seafile-data", NULL);
seaf = seafile_session_new(seafile_dir, ccnet_client);
if (!seaf) {
g_warning ("Failed to create seafile session.\n");
exit (1);
}
seafile_session_init(seaf);
if (load_content_type_map(&ftmap) < 0) {
g_warning ("load content type error\n");
return -1;
}
evbase = event_base_new();
htp = evhtp_new(evbase, NULL);
evhtp_set_regex_cb (htp, "^/files/.*", access_cb, NULL);
evhtp_set_gencb(htp, default_cb, NULL);
evhtp_use_threads(htp, NULL, num_threads, NULL);
if (evhtp_bind_socket(htp, bind_addr, bind_port, 128) < 0) {
g_warning ("Could not bind socket: %s\n", strerror(errno));
exit(-1);
}
event_base_loop(evbase, 0);
return 0;
}

9
httpserver/httpserver.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef HTTPSERVER_H
#define HTTPSERVER_H
struct file_type_map {
char *suffix;
char *type;
};
#endif /* HTTPSERVER_H */

823
httpserver/repo-mgr.c Normal file
View File

@ -0,0 +1,823 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include <glib/gstdio.h>
#include <ccnet.h>
#include "utils.h"
#include "avl/avl.h"
#include "log.h"
#include "seafile-session.h"
#include "seafile-config.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#include "repo-mgr.h"
#include "fs-mgr.h"
#include "seafile-error.h"
#include "gc.h"
#include "seafile-crypt.h"
#include "index/index.h"
#include "index/cache-tree.h"
#include "unpack-trees.h"
#include "diff-simple.h"
#include "seaf-db.h"
#define INDEX_DIR "index"
struct _SeafRepoManagerPriv {
avl_tree_t *repo_tree;
pthread_rwlock_t lock;
};
static SeafRepo *
load_repo (SeafRepoManager *manager, const char *repo_id);
static int create_db_tables_if_not_exist (SeafRepoManager *mgr);
gboolean
is_repo_id_valid (const char *id)
{
if (!id)
return FALSE;
return is_uuid_valid (id);
}
SeafRepo*
seaf_repo_new (const char *id, const char *name, const char *desc)
{
SeafRepo* repo;
/* valid check */
repo = g_new0 (SeafRepo, 1);
memcpy (repo->id, id, 36);
repo->id[36] = '\0';
repo->name = g_strdup(name);
repo->desc = g_strdup(desc);
repo->ref_cnt = 1;
return repo;
}
void
seaf_repo_free (SeafRepo *repo)
{
if (repo->name) g_free (repo->name);
if (repo->desc) g_free (repo->desc);
if (repo->category) g_free (repo->category);
if (repo->head) seaf_branch_unref (repo->head);
g_free (repo);
}
void
seaf_repo_ref (SeafRepo *repo)
{
g_atomic_int_inc (&repo->ref_cnt);
}
void
seaf_repo_unref (SeafRepo *repo)
{
if (g_atomic_int_dec_and_test (&repo->ref_cnt))
seaf_repo_free (repo);
}
static void
set_head_common (SeafRepo *repo, SeafBranch *branch, SeafCommit *commit)
{
if (repo->head)
seaf_branch_unref (repo->head);
repo->head = branch;
seaf_branch_ref(branch);
}
void
seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit)
{
repo->name = g_strdup (commit->repo_name);
repo->desc = g_strdup (commit->repo_desc);
repo->encrypted = commit->encrypted;
if (repo->encrypted) {
repo->enc_version = commit->enc_version;
if (repo->enc_version >= 1)
memcpy (repo->magic, commit->magic, 33);
}
repo->no_local_history = commit->no_local_history;
}
void
seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit)
{
commit->repo_name = g_strdup (repo->name);
commit->repo_desc = g_strdup (repo->desc);
commit->encrypted = repo->encrypted;
if (commit->encrypted) {
commit->enc_version = repo->enc_version;
if (commit->enc_version >= 1)
commit->magic = g_strdup (repo->magic);
}
commit->no_local_history = repo->no_local_history;
}
static gboolean
collect_commit (SeafCommit *commit, void *vlist, gboolean *stop)
{
GList **commits = vlist;
/* The traverse function will unref the commit, so we need to ref it.
*/
seaf_commit_ref (commit);
*commits = g_list_prepend (*commits, commit);
return TRUE;
}
GList *
seaf_repo_get_commits (SeafRepo *repo)
{
GList *branches;
GList *ptr;
SeafBranch *branch;
GList *commits = NULL;
branches = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo->id);
if (branches == NULL) {
g_warning ("Failed to get branch list of repo %s.\n", repo->id);
return NULL;
}
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
branch = ptr->data;
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
branch->commit_id,
collect_commit,
&commits);
if (!res) {
for (ptr = commits; ptr != NULL; ptr = ptr->next)
seaf_commit_unref ((SeafCommit *)(ptr->data));
g_list_free (commits);
goto out;
}
}
commits = g_list_reverse (commits);
out:
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
seaf_branch_unref ((SeafBranch *)ptr->data);
}
return commits;
}
static int
compare_repo (const SeafRepo *srepo, const SeafRepo *trepo)
{
return g_strcmp0 (srepo->id, trepo->id);
}
SeafRepoManager*
seaf_repo_manager_new (SeafileSession *seaf)
{
SeafRepoManager *mgr = g_new0 (SeafRepoManager, 1);
mgr->priv = g_new0 (SeafRepoManagerPriv, 1);
mgr->seaf = seaf;
mgr->priv->repo_tree = avl_alloc_tree ((avl_compare_t)compare_repo,
NULL);
pthread_rwlock_init (&mgr->priv->lock, NULL);
return mgr;
}
int
seaf_repo_manager_init (SeafRepoManager *mgr)
{
/* On the server, we load repos into memory on-demand, because
* there are too many repos.
*/
if (create_db_tables_if_not_exist (mgr) < 0) {
g_warning ("[repo mgr] failed to create tables.\n");
return -1;
}
return 0;
}
int
seaf_repo_manager_start (SeafRepoManager *mgr)
{
return 0;
}
static gboolean
repo_exists_in_db (SeafDB *db, const char *id)
{
char sql[256];
snprintf (sql, sizeof(sql), "SELECT repo_id FROM Repo WHERE repo_id = '%s'",
id);
return seaf_db_check_for_existence (db, sql);
}
SeafRepo*
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id)
{
SeafRepo repo;
int len = strlen(id);
if (len >= 37)
return NULL;
memcpy (repo.id, id, len + 1);
#if 0
if (pthread_rwlock_rdlock (&manager->priv->lock) < 0) {
g_warning ("[repo mgr] failed to lock repo cache.\n");
return NULL;
}
avl_node_t *res = avl_search (manager->priv->repo_tree, &repo);
pthread_rwlock_unlock (&manager->priv->lock);
if (res) {
seaf_repo_ref ((SeafRepo *)(res->item));
return res->item;
}
#endif
if (repo_exists_in_db (manager->seaf->db, id)) {
SeafRepo *ret = load_repo (manager, id);
if (!ret)
return NULL;
/* seaf_repo_ref (ret); */
return ret;
}
return NULL;
}
SeafRepo*
seaf_repo_manager_get_repo_prefix (SeafRepoManager *manager, const gchar *id)
{
avl_node_t *node;
SeafRepo repo, *result;
int len = strlen(id);
if (len >= 37)
return NULL;
memcpy (repo.id, id, len + 1);
avl_search_closest (manager->priv->repo_tree, &repo, &node);
if (node != NULL) {
result = node->item;
if (strncmp (id, result->id, len) == 0)
return node->item;
}
return NULL;
}
gboolean
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id)
{
SeafRepo repo;
memcpy (repo.id, id, 37);
#if 0
if (pthread_rwlock_rdlock (&manager->priv->lock) < 0) {
g_warning ("[repo mgr] failed to lock repo cache.\n");
return FALSE;
}
avl_node_t *res = avl_search (manager->priv->repo_tree, &repo);
pthread_rwlock_unlock (&manager->priv->lock);
if (res)
return TRUE;
#endif
return repo_exists_in_db (manager->seaf->db, id);
}
gboolean
seaf_repo_manager_repo_exists_prefix (SeafRepoManager *manager, const gchar *id)
{
avl_node_t *node;
SeafRepo repo;
memcpy (repo.id, id, 37);
avl_search_closest (manager->priv->repo_tree, &repo, &node);
if (node != NULL)
return TRUE;
return FALSE;
}
static void
load_repo_commit (SeafRepoManager *manager,
SeafRepo *repo,
SeafBranch *branch)
{
SeafCommit *commit;
commit = seaf_commit_manager_get_commit (manager->seaf->commit_mgr,
branch->commit_id);
if (!commit) {
g_warning ("Commit %s is missing\n", branch->commit_id);
repo->is_corrupted = TRUE;
return;
}
set_head_common (repo, branch, commit);
seaf_repo_from_commit (repo, commit);
seaf_commit_unref (commit);
}
static gboolean
load_branch_cb (SeafDBRow *row, void *vrepo)
{
SeafRepo *repo = vrepo;
SeafRepoManager *manager = repo->manager;
const char *branch_name = seaf_db_row_get_column_text (row, 0);
SeafBranch *branch =
seaf_branch_manager_get_branch (manager->seaf->branch_mgr,
repo->id, branch_name);
if (branch == NULL) {
g_warning ("Broken branch name for repo %s\n", repo->id);
repo->is_corrupted = TRUE;
return FALSE;
}
load_repo_commit (manager, repo, branch);
seaf_branch_unref (branch);
/* Only one result. */
return FALSE;
}
static SeafRepo *
load_repo (SeafRepoManager *manager, const char *repo_id)
{
char sql[256];
SeafRepo *repo = seaf_repo_new(repo_id, NULL, NULL);
if (!repo) {
g_warning ("[repo mgr] failed to alloc repo.\n");
return NULL;
}
repo->manager = manager;
snprintf(sql, 256, "SELECT branch_name FROM RepoHead WHERE repo_id='%s'",
repo->id);
if (seaf_db_foreach_selected_row (seaf->db, sql, load_branch_cb, repo) < 0) {
g_warning ("Error read branch for repo %s.\n", repo->id);
seaf_repo_unref (repo);
return NULL;
}
if (repo->is_corrupted) {
seaf_repo_free (repo);
return NULL;
}
#if 0
if (pthread_rwlock_wrlock (&manager->priv->lock) < 0) {
g_warning ("[repo mgr] failed to lock repo cache.\n");
seaf_repo_free (repo);
return NULL;
}
avl_insert (manager->priv->repo_tree, repo);
/* Don't need to increase ref count, since the ref count of
* a new repo object is already 1.
*/
pthread_rwlock_unlock (&manager->priv->lock);
#endif
return repo;
}
static int
create_db_tables_if_not_exist (SeafRepoManager *mgr)
{
SeafDB *db = mgr->seaf->db;
char *sql = "CREATE TABLE IF NOT EXISTS Repo (repo_id CHAR(37) PRIMARY KEY)";
if (seaf_db_query (db, sql) < 0)
return -1;
int db_type = seaf_db_type (db);
if (db_type == SEAF_DB_TYPE_MYSQL) {
sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
"repo_id CHAR(37) PRIMARY KEY, "
"owner_id VARCHAR(255),"
"INDEX (owner_id))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE RepoGroup (repo_id CHAR(37), group_id INTEGER, "
"user_name VARCHAR(255), permission CHAR(15), "
"UNIQUE INDEX (group_id, repo_id))";
if (seaf_db_query (db, sql) < 0)
return -1;
} else if (db_type == SEAF_DB_TYPE_SQLITE) {
sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
"repo_id CHAR(37) PRIMARY KEY, "
"owner_id TEXT)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE INDEX IF NOT EXISTS OwnerIndex ON RepoOwner (owner_id)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS RepoGroup (repo_id CHAR(37), "
"group_id INTEGER, user_name TEXT, permission CHAR(15))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupid_repoid_indx on "
"RepoGroup (group_id, repo_id)";
if (seaf_db_query (db, sql) < 0)
return -1;
}
sql = "CREATE TABLE IF NOT EXISTS RepoHead ("
"repo_id CHAR(37) PRIMARY KEY, branch_name VARCHAR(10))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS RepoToken ("
"repo_id CHAR(37) PRIMARY KEY, token VARCHAR(65))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS PublicRepo (repo_id CHAR(37) PRIMARY KEY)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS WebAP (repo_id CHAR(37) PRIMARY KEY, "
"access_property CHAR(10))";
if (seaf_db_query (db, sql) < 0)
return -1;
return 0;
}
int
seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr,
const char *repo_id,
const char *email)
{
char sql[256];
snprintf (sql, sizeof(sql), "REPLACE INTO RepoOwner VALUES ('%s', '%s')",
repo_id, email);
if (seaf_db_query (mgr->seaf->db, sql) < 0)
return -1;
return 0;
}
static gboolean
get_owner (SeafDBRow *row, void *data)
{
char **owner_id = data;
*owner_id = g_strdup(seaf_db_row_get_column_text (row, 0));
/* There should be only one result. */
return FALSE;
}
char *
seaf_repo_manager_get_repo_owner (SeafRepoManager *mgr,
const char *repo_id)
{
char sql[256];
char *ret = NULL;
snprintf (sql, sizeof(sql),
"SELECT owner_id FROM RepoOwner WHERE repo_id='%s'",
repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
get_owner, &ret) < 0) {
g_warning ("Failed to get owner id for repo %s.\n", repo_id);
return NULL;
}
return ret;
}
static gboolean
collect_repos (SeafDBRow *row, void *data)
{
GList **p_repos = data;
const char *repo_id;
SeafRepo *repo;
repo_id = seaf_db_row_get_column_text (row, 0);
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
if (!repo) {
/* Continue to collect the remaining repos. */
return TRUE;
}
*p_repos = g_list_prepend (*p_repos, repo);
return TRUE;
}
GList *
seaf_repo_manager_get_repos_by_owner (SeafRepoManager *mgr,
const char *email)
{
GList *ret = NULL;
char sql[256];
snprintf (sql, 256, "SELECT repo_id FROM RepoOwner WHERE owner_id='%s'",
email);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
collect_repos, &ret) < 0)
return NULL;
return g_list_reverse (ret);
}
static gboolean
collect_repo_id (SeafDBRow *row, void *data)
{
GList **p_ids = data;
const char *repo_id;
repo_id = seaf_db_row_get_column_text (row, 0);
*p_ids = g_list_prepend (*p_ids, g_strdup(repo_id));
return TRUE;
}
GList *
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr)
{
GList *ret = NULL;
char sql[256];
snprintf (sql, 256, "SELECT repo_id FROM Repo");
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
collect_repo_id, &ret) < 0)
return NULL;
return ret;
}
GList *
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit)
{
GList *ret = NULL;
char sql[256];
if (start == -1 && limit == -1)
snprintf (sql, 256, "SELECT repo_id FROM Repo");
else
snprintf (sql, 256, "SELECT repo_id FROM Repo LIMIT %d, %d", start, limit);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
collect_repos, &ret) < 0)
return NULL;
return g_list_reverse (ret);
}
static gboolean
get_repo_size (SeafDBRow *row, void *vsize)
{
gint64 *psize = vsize;
*psize = seaf_db_row_get_column_int64 (row, 0);
return FALSE;
}
gint64
seaf_repo_manager_get_repo_size (SeafRepoManager *mgr, const char *repo_id)
{
gint64 size = 0;
char sql[256];
snprintf (sql, sizeof(sql), "SELECT size FROM RepoSize WHERE repo_id='%s'",
repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
get_repo_size, &size) < 0)
return -1;
return size;
}
int
seaf_repo_manager_set_access_property (SeafRepoManager *mgr, const char *repo_id,
const char *ap)
{
char sql[256];
if (seaf_repo_manager_query_access_property (mgr, repo_id) == NULL) {
snprintf (sql, sizeof(sql), "INSERT INTO WebAP VALUES ('%s', '%s')",
repo_id, ap);
} else {
snprintf (sql, sizeof(sql), "UPDATE WebAP SET access_property='%s' "
"WHERE repo_id='%s'", ap, repo_id);
}
if (seaf_db_query (mgr->seaf->db, sql) < 0) {
g_warning ("DB error when set access property for repo %s, %s.\n", repo_id, ap);
return -1;
}
return 0;
}
static gboolean
get_ap (SeafDBRow *row, void *data)
{
char **ap = data;
*ap = g_strdup (seaf_db_row_get_column_text (row, 0));
return FALSE;
}
char *
seaf_repo_manager_query_access_property (SeafRepoManager *mgr, const char *repo_id)
{
char sql[256];
char *ret = NULL;
snprintf (sql, sizeof(sql), "SELECT access_property FROM WebAP WHERE repo_id='%s'",
repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql, get_ap, &ret) < 0) {
g_warning ("DB error when get access property for repo %s.\n", repo_id);
return NULL;
}
return ret;
}
int
seaf_repo_manager_share_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
const char *permission,
GError **error)
{
char sql[512];
snprintf (sql, sizeof(sql), "INSERT INTO RepoGroup VALUES ('%s', %d, '%s', '%s')",
repo_id, group_id, user_name, permission);
if (seaf_db_query (mgr->seaf->db, sql) < 0)
return -1;
return 0;
}
int
seaf_repo_manager_unshare_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
GError **error)
{
char sql[512];
snprintf (sql, sizeof(sql), "DELETE FROM RepoGroup WHERE repo_id='%s' "
"AND group_id=%d", repo_id, group_id);
return seaf_db_query (mgr->seaf->db, sql);
}
static gboolean
get_group_repoids_cb (SeafDBRow *row, void *data)
{
GList **p_list = data;
char *repo_id = g_strdup ((const char *)seaf_db_row_get_column_text (row, 0));
*p_list = g_list_prepend (*p_list, repo_id);
return TRUE;
}
GList *
seaf_repo_manager_get_group_repoids (SeafRepoManager *mgr,
int group_id,
GError **error)
{
char sql[512];
GList *repo_ids = NULL;
snprintf (sql, sizeof(sql), "SELECT repo_id FROM RepoGroup "
"WHERE group_id = %d", group_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql, get_group_repoids_cb,
&repo_ids) < 0)
return NULL;
return g_list_reverse (repo_ids);
}
static gboolean
get_group_repos_cb (SeafDBRow *row, void *data)
{
GList **p_list = data;
SeafileRepoGroup *repo_group = NULL;
char *repo_id = g_strdup ((const char *)seaf_db_row_get_column_text (row, 0));
int group_id = seaf_db_row_get_column_int (row, 1);
char *user_name = g_strdup ((const char *)seaf_db_row_get_column_text (row, 2));
repo_group = g_object_new (SEAFILE_TYPE_REPO_GROUP,
"repo_id", repo_id,
"group_id", group_id,
"user_name", user_name,
NULL);
if (repo_group != NULL) {
/* g_object_ref (repo_group); */
*p_list = g_list_prepend (*p_list, repo_group);
}
return TRUE;
}
GList *
seaf_repo_manager_get_group_my_share_repos (SeafRepoManager *mgr,
const char *username,
GError **error)
{
char sql[512];
GList *repos = NULL;
snprintf (sql, sizeof(sql), "SELECT repo_id, group_id, user_name "
"FROM RepoGroup WHERE user_name = '%s'", username);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql, get_group_repos_cb,
&repos) < 0)
return NULL;
return g_list_reverse (repos);
}
static gboolean
get_repo_share_from (SeafDBRow *row, void *data)
{
char **share_from = data;
*share_from = g_strdup (seaf_db_row_get_column_text (row, 0));
/* There should be only one result. */
return FALSE;
}
char *
seaf_repo_manager_get_repo_share_from (SeafRepoManager *mgr,
const char *repo_id,
GError **error)
{
char sql[512];
char *ret = NULL;
snprintf (sql, sizeof(sql), "SELECT user_name FROM RepoGroup "
"WHERE repo_id = '%s'", repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
get_repo_share_from, &ret) < 0) {
g_warning ("DB error when get repo share from for repo %s.\n",
repo_id);
return NULL;
}
return ret;
}
int
seaf_repo_manager_remove_repo_group (SeafRepoManager *mgr,
int group_id,
const char *user_name,
GError **error)
{
char sql[512];
if (!user_name) {
snprintf (sql, sizeof(sql), "DELETE FROM RepoGroup WHERE group_id=%d",
group_id);
} else {
snprintf (sql, sizeof(sql), "DELETE FROM RepoGroup WHERE group_id=%d AND "
"user_name = '%s'", group_id, user_name);
}
return seaf_db_query (mgr->seaf->db, sql);
}

162
httpserver/repo-mgr.h Normal file
View File

@ -0,0 +1,162 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef SEAF_REPO_MGR_H
#define SEAF_REPO_MGR_H
#include <pthread.h>
#include "seafile-object.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#define REPO_AUTO_SYNC "auto-sync"
#define REPO_AUTO_FETCH "auto-fetch"
#define REPO_AUTO_UPLOAD "auto-upload"
#define REPO_AUTO_MERGE "auto-merge"
#define REPO_AUTO_COMMIT "auto-commit"
#define REPO_RELAY_ID "relay-id"
#define REPO_NET_BROWSABLE "net-browsable"
#define REPO_DOUBLE_SYNC "double-sync"
#define REPO_REMOTE_HEAD "remote-head"
#define REPO_ENCRYPTED 0x1
struct _SeafRepoManager;
typedef struct _SeafRepo SeafRepo;
struct _SeafRepo {
struct _SeafRepoManager *manager;
gchar id[37];
gchar *name;
gchar *desc;
gchar *category; /* not used yet */
gboolean encrypted;
int enc_version;
gchar magic[33]; /* hash(repo_id + passwd), key stretched. */
gboolean no_local_history;
SeafBranch *head;
gboolean is_corrupted;
gboolean delete_pending;
int ref_cnt;
};
gboolean is_repo_id_valid (const char *id);
SeafRepo*
seaf_repo_new (const char *id, const char *name, const char *desc);
void
seaf_repo_free (SeafRepo *repo);
void
seaf_repo_ref (SeafRepo *repo);
void
seaf_repo_unref (SeafRepo *repo);
typedef struct _SeafRepoManager SeafRepoManager;
typedef struct _SeafRepoManagerPriv SeafRepoManagerPriv;
struct _SeafRepoManager {
struct _SeafileSession *seaf;
SeafRepoManagerPriv *priv;
};
SeafRepoManager*
seaf_repo_manager_new (struct _SeafileSession *seaf);
int
seaf_repo_manager_init (SeafRepoManager *mgr);
int
seaf_repo_manager_start (SeafRepoManager *mgr);
int
seaf_repo_manager_add_repo (SeafRepoManager *mgr, SeafRepo *repo);
int
seaf_repo_manager_del_repo (SeafRepoManager *mgr, SeafRepo *repo);
SeafRepo*
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id);
SeafRepo*
seaf_repo_manager_get_repo_prefix (SeafRepoManager *manager, const gchar *id);
gboolean
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id);
gboolean
seaf_repo_manager_repo_exists_prefix (SeafRepoManager *manager, const gchar *id);
GList*
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit);
int
seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr,
const char *repo_id,
const char *email);
char *
seaf_repo_manager_get_repo_owner (SeafRepoManager *mgr,
const char *repo_id);
/* TODO: add start and limit. */
/* Get repos owned by this user.
*/
GList *
seaf_repo_manager_get_repos_by_owner (SeafRepoManager *mgr,
const char *email);
GList *
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr);
gint64
seaf_repo_manager_get_repo_size (SeafRepoManager *mgr, const char *repo_id);
int
seaf_repo_manager_set_access_property (SeafRepoManager *mgr, const char *repo_id,
const char *ap);
char *
seaf_repo_manager_query_access_property (SeafRepoManager *mgr, const char *repo_id);
int
seaf_repo_manager_share_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
const char *permission,
GError **error);
int
seaf_repo_manager_unshare_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
GError **error);
GList *
seaf_repo_manager_get_group_repoids (SeafRepoManager *mgr,
int group_id,
GError **error);
GList *
seaf_repo_manager_get_group_my_share_repos (SeafRepoManager *mgr,
const char *username,
GError **error);
char *
seaf_repo_manager_get_repo_share_from (SeafRepoManager *mgr,
const char *repo_id,
GError **error);
int
seaf_repo_manager_remove_repo_group (SeafRepoManager *mgr,
int group_id,
const char *user_name,
GError **error);
#endif

View File

@ -0,0 +1,105 @@
#include "common.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ccnet.h>
#include <utils.h>
#include "seafile-session.h"
#include "seafile-config.h"
#include "seaf-utils.h"
SeafileSession *
seafile_session_new(const char *seafile_dir,
CcnetClient *ccnet_session)
{
char *abs_seafile_dir;
char *tmp_file_dir;
char *config_file_path;
struct stat st;
GKeyFile *config;
SeafileSession *session;
if (!ccnet_session)
return NULL;
abs_seafile_dir = ccnet_expand_path (seafile_dir);
tmp_file_dir = g_build_filename (abs_seafile_dir, "tmpfiles", NULL);
config_file_path = g_build_filename (abs_seafile_dir, "seafile.conf", NULL);
if (g_lstat(abs_seafile_dir, &st) < 0 || !S_ISDIR(st.st_mode)) {
g_warning ("Seafile data dir %s does not exist and is unable to create\n",
abs_seafile_dir);
goto onerror;
}
if (g_lstat(tmp_file_dir, &st) < 0 || !S_ISDIR(st.st_mode)) {
g_warning ("Seafile tmp dir %s does not exist and is unable to create\n",
tmp_file_dir);
goto onerror;
}
GError *error = NULL;
config = g_key_file_new ();
if (!g_key_file_load_from_file (config, config_file_path,
G_KEY_FILE_NONE, &error)) {
g_warning ("Failed to load config file.\n");
g_key_file_free (config);
goto onerror;
}
session = g_new0(SeafileSession, 1);
session->seaf_dir = abs_seafile_dir;
session->tmp_file_dir = tmp_file_dir;
session->session = ccnet_session;
session->config = config;
if (load_database_config (session) < 0) {
g_warning ("Failed to load database config.\n");
goto onerror;
}
session->fs_mgr = seaf_fs_manager_new (session, abs_seafile_dir);
if (!session->fs_mgr)
goto onerror;
session->block_mgr = seaf_block_manager_new (session, abs_seafile_dir);
if (!session->block_mgr)
goto onerror;
session->commit_mgr = seaf_commit_manager_new (session);
if (!session->commit_mgr)
goto onerror;
session->repo_mgr = seaf_repo_manager_new (session);
if (!session->repo_mgr)
goto onerror;
session->branch_mgr = seaf_branch_manager_new (session);
if (!session->branch_mgr)
goto onerror;
return session;
onerror:
free (abs_seafile_dir);
g_free (tmp_file_dir);
g_free (config_file_path);
g_free (session);
return NULL;
}
int
seafile_session_init (SeafileSession *session)
{
seaf_commit_manager_init (session->commit_mgr);
seaf_fs_manager_init (session->fs_mgr);
seaf_branch_manager_init (session->branch_mgr);
seaf_repo_manager_init (session->repo_mgr);
return 0;
}
int
seafile_session_start (SeafileSession *session)
{
return 0;
}

View File

@ -0,0 +1,47 @@
#ifndef SEAFILE_SESSION_H
#define SEAFILE_SESSION_H
#include <stdint.h>
#include <glib.h>
#include "block-mgr.h"
#include "fs-mgr.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#include "repo-mgr.h"
#include "db.h"
#include "seaf-db.h"
struct _CcnetClient;
typedef struct _SeafileSession SeafileSession;
struct _SeafileSession {
struct _CcnetClient *session;
char *seaf_dir;
char *tmp_file_dir;
/* Config that's only loaded on start */
GKeyFile *config;
SeafDB *db;
SeafBlockManager *block_mgr;
SeafFSManager *fs_mgr;
SeafCommitManager *commit_mgr;
SeafBranchManager *branch_mgr;
SeafRepoManager *repo_mgr;
};
extern SeafileSession *seaf;
SeafileSession *
seafile_session_new (const char *seafile_dir,
struct _CcnetClient *ccnet);
int
seafile_session_init (SeafileSession *session);
int
seafile_session_start (SeafileSession *session);
#endif

55
monitor/Makefile.am Normal file
View File

@ -0,0 +1,55 @@
AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \
-DSEAFILE_SERVER \
-DSEAFILE_MONITOR \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/common \
@CCNET_CFLAGS@ \
@SEARPC_CFLAGS@ \
@GLIB2_CFLAGS@ \
@MYSQL_CFLAGS@ \
@ZDB_CFLAGS@ \
@CURL_CFLAGS@ \
-Wall
bin_PROGRAMS = seaf-mon
noinst_HEADERS = \
seafile-session.h \
repo-mgr.h \
scheduler.h
seaf_mon_SOURCES = \
seaf-mon.c \
seafile-session.c \
repo-mgr.c \
scheduler.c \
monitor-rpc.c \
../common/seaf-db.c \
../common/bitfield.c \
../common/branch-mgr.c \
../common/fs-mgr.c \
../common/block-mgr.c \
../common/block-backend.c \
../common/block-backend-fs.c \
../common/block-backend-ceph.c \
../common/commit-mgr.c \
../common/avl/avl.c \
../common/log.c \
../common/seaf-utils.c \
../common/obj-store.c \
../common/obj-backend-fs.c \
../common/obj-backend-riak.c \
../common/riak-http-client.c \
../common/seafile-crypt.c \
../common/mq-mgr.c
seaf_mon_LDADD = @CCNET_LIBS@ \
$(top_builddir)/common/cdc/libcdc.la \
$(top_builddir)/lib/libseafile_common.la \
@GLIB2_LIBS@ @GOBJECT_LIBS@ -lssl @LIB_RT@ @LIB_UUID@ -lsqlite3 -levent \
@MYSQL_LIBS@ @SEARPC_LIBS@ @ZDB_LIBS@ @RADOS_LIBS@ @CURL_LIBS@
seaf_mon_LDFLAGS = @STATIC_COMPILE@ @SERVER_PKG_RPATH@

19
monitor/monitor-rpc.c Normal file
View File

@ -0,0 +1,19 @@
#include <ccnet.h>
#include "seafile-session.h"
#include "seafile-error.h"
#define SEAFILE_DOMAIN g_quark_from_string("MONITOR")
int
monitor_compute_repo_size (const char *repo_id, GError **error)
{
if (!repo_id) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Argument should not be null");
return -1;
}
schedule_repo_size_computation (seaf->scheduler, repo_id);
return 0;
}

View File

@ -0,0 +1,130 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include <ccnet.h>
#include "monitor.h"
#include "heartbeat-proc.h"
#define SC_BLOCK_INFO "301"
#define SS_BLOCK_INFO "Block info"
typedef struct {
int dummy;
} SeafileHeartbeatProcPriv;
static void
process_block_info (char *info, int len);
#define GET_PRIV(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_HEARTBEAT_PROC, SeafileHeartbeatProcPriv))
#define USE_PRIV \
SeafileHeartbeatProcPriv *priv = GET_PRIV(processor);
G_DEFINE_TYPE (SeafileHeartbeatProc, seafile_heartbeat_proc, CCNET_TYPE_PROCESSOR)
static int start (CcnetProcessor *processor, int argc, char **argv);
static void handle_update (CcnetProcessor *processor,
char *code, char *code_msg,
char *content, int clen);
static void
release_resource(CcnetProcessor *processor)
{
/* FILL IT */
CCNET_PROCESSOR_CLASS (seafile_heartbeat_proc_parent_class)->release_resource (processor);
}
static void
seafile_heartbeat_proc_class_init (SeafileHeartbeatProcClass *klass)
{
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
proc_class->start = start;
proc_class->handle_update = handle_update;
proc_class->release_resource = release_resource;
g_type_class_add_private (klass, sizeof (SeafileHeartbeatProcPriv));
}
static void
seafile_heartbeat_proc_init (SeafileHeartbeatProc *processor)
{
}
static int
start (CcnetProcessor *processor, int argc, char **argv)
{
ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0);
return 0;
}
static void
handle_update (CcnetProcessor *processor,
char *code, char *code_msg,
char *content, int clen)
{
if (strncmp(code, SC_BLOCK_INFO, 3) == 0) {
if (content[clen-1] != '\0') {
g_warning ("[heartbeat] Bad content format.\n");
/* Tolerate bad format, don't stop the processor. */
return;
}
process_block_info (content, clen);
return;
}
g_warning ("[heartbeat] Bad update: %s %s.\n", code, code_msg);
ccnet_processor_done (processor, FALSE);
}
static void
set_block_size (char *line)
{
char *block_id, *size_str;
uint32_t size;
char *space;
space = strchr (line, ' ');
if (!space || space - line != 40) {
g_warning ("[heartbeat] Bad block size line format\n");
return;
}
*space = '\0';
block_id = line;
size_str = space + 1;
if (sscanf (size_str, "%u", &size) < 1) {
g_warning ("[heartbeat] Bad block size format\n");
return;
}
g_message ("[heartbeat] Setting block size: %s %s\n", block_id, size_str);
seaf_monitor_set_block_size (singleton_monitor, block_id, size);
}
static void
process_block_info (char *info, int len)
{
char *line;
char *saveptr;
line = strtok_r (info, "\n", &saveptr);
if (!line)
return;
set_block_size (line);
while (1) {
line = strtok_r (NULL, "\n", &saveptr);
if (!line)
break;
set_block_size (line);
}
}

View File

@ -0,0 +1,30 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef SEAFILE_HEARTBEAT_PROC_H
#define SEAFILE_HEARTBEAT_PROC_H
#include <glib-object.h>
#include <ccnet/processor.h>
#define SEAFILE_TYPE_HEARTBEAT_PROC (seafile_heartbeat_proc_get_type ())
#define SEAFILE_HEARTBEAT_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_HEARTBEAT_PROC, SeafileHeartbeatProc))
#define SEAFILE_IS_HEARTBEAT_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_HEARTBEAT_PROC))
#define SEAFILE_HEARTBEAT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_HEARTBEAT_PROC, SeafileHeartbeatProcClass))
#define IS_SEAFILE_HEARTBEAT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_HEARTBEAT_PROC))
#define SEAFILE_HEARTBEAT_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_HEARTBEAT_PROC, SeafileHeartbeatProcClass))
typedef struct _SeafileHeartbeatProc SeafileHeartbeatProc;
typedef struct _SeafileHeartbeatProcClass SeafileHeartbeatProcClass;
struct _SeafileHeartbeatProc {
CcnetProcessor parent_instance;
};
struct _SeafileHeartbeatProcClass {
CcnetProcessorClass parent_class;
};
GType seafile_heartbeat_proc_get_type ();
#endif

View File

@ -0,0 +1,229 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <stdlib.h>
#include <string.h>
#include "monitor.h"
#include "repostat-proc.h"
#define SC_BLOCK_LIST "301"
#define SS_BLOCK_LIST "Block list"
#define SC_BLOCK_LIST_END "302"
#define SS_BLOCK_LIST_END "Block list end"
#define SC_FINISHED "303"
#define SS_FINISHED "Finished"
#define SC_DB_ERROR "401"
#define SS_DB_ERROR "Database error"
#define SC_BAD_BL "402"
#define SS_BAD_BL "Bad block list format"
typedef struct {
char repo_id[41];
char head[41];
GPtrArray *block_ids;
uint64_t repo_size;
int is_accurate;
} SeafileRepostatProcPriv;
#define GET_PRIV(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SEAFILE_TYPE_REPOSTAT_PROC, SeafileRepostatProcPriv))
#define USE_PRIV \
SeafileRepostatProcPriv *priv = GET_PRIV(processor);
G_DEFINE_TYPE (SeafileRepostatProc, seafile_repostat_proc, CCNET_TYPE_PROCESSOR)
static int start (CcnetProcessor *processor, int argc, char **argv);
static void handle_update (CcnetProcessor *processor,
char *code, char *code_msg,
char *content, int clen);
static void process_block_list (CcnetProcessor *processor,
char *list,
int len);
static int start_compute (CcnetProcessor *processor);
static void computation_finished (CcnetProcessor *processor,
int status,
char *message);
static void
release_resource(CcnetProcessor *processor)
{
USE_PRIV;
ccnet_processor_thread_cancel (processor);
if (priv->block_ids)
g_ptr_array_free (priv->block_ids, TRUE);
CCNET_PROCESSOR_CLASS (seafile_repostat_proc_parent_class)->release_resource (processor);
}
static void
seafile_repostat_proc_class_init (SeafileRepostatProcClass *klass)
{
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
proc_class->start = start;
proc_class->handle_update = handle_update;
proc_class->handle_thread_done = computation_finished;
proc_class->release_resource = release_resource;
g_type_class_add_private (klass, sizeof (SeafileRepostatProcPriv));
}
static void
seafile_repostat_proc_init (SeafileRepostatProc *processor)
{
}
static int
start (CcnetProcessor *processor, int argc, char **argv)
{
USE_PRIV;
if (argc != 2 || strlen(argv[0]) != 36 || strlen(argv[1]) != 40) {
ccnet_processor_send_response (processor, SC_BAD_ARGS, SS_BAD_ARGS, NULL, 0);
ccnet_processor_done (processor, FALSE);
return -1;
}
if (seaf_monitor_is_repo_size_uptodate (singleton_monitor,
argv[0], argv[1])) {
g_message ("repo size %s is up-to-date.\n", argv[0]);
ccnet_processor_send_response (processor, SC_FINISHED, SS_FINISHED, NULL, 0);
ccnet_processor_done (processor, TRUE);
return 0;
}
memcpy (priv->repo_id, argv[0], 41);
memcpy (priv->head, argv[1], 41);
priv->block_ids = g_ptr_array_new_with_free_func (g_free);
priv->is_accurate = 1;
ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0);
return 0;
}
static void
handle_update (CcnetProcessor *processor,
char *code, char *code_msg,
char *content, int clen)
{
if (strncmp (code, SC_BLOCK_LIST, 3) == 0) {
process_block_list (processor, content, clen);
} else if (strncmp (code, SC_BLOCK_LIST_END, 3) == 0) {
process_block_list (processor, content, clen);
start_compute (processor);
} else {
g_warning ("[repo stat] Bad update: %s %s.\n", code, code_msg);
ccnet_processor_done (processor, FALSE);
}
}
static void
process_block_list (CcnetProcessor *processor, char *list, int len)
{
USE_PRIV;
char *block_id;
int n_blocks;
int i;
if (len % 41 != 1 || list[len - 1] != '\0') {
ccnet_processor_send_response (processor, SC_BAD_BL, SS_BAD_BL, NULL, 0);
ccnet_processor_done (processor, FALSE);
return;
}
n_blocks = len/41;
block_id = list;
for (i = 0; i < n_blocks; ++i) {
block_id[40] = '\0';
g_ptr_array_add (priv->block_ids, g_strdup(block_id));
block_id += 41;
}
}
static void *
compute_repo_size (void *vprocessor)
{
CcnetProcessor *processor = vprocessor;
USE_PRIV;
uint32_t n_blocks, i;
uint64_t repo_size = 0;
char *block_id;
uint32_t block_size;
int status = 0;
g_message ("Compute size of repo %s.\n", priv->repo_id);
n_blocks = priv->block_ids->len;
for (i = 0; i < n_blocks; ++i) {
block_id = g_ptr_array_index (priv->block_ids, i);
block_size = seaf_monitor_get_block_size (singleton_monitor, block_id);
/*
* If we cannot get accurate block size, use avg block size (1MB).
*/
if (block_size == 0) {
block_size = 1 << 20;
priv->is_accurate = 0;
}
repo_size += block_size;
}
priv->repo_size = repo_size;
ccnet_processor_thread_done (processor, status, NULL);
return NULL;
}
static int
start_compute (CcnetProcessor *processor)
{
int rc;
rc = ccnet_processor_thread_create (processor,
compute_repo_size,
NULL);
if (rc < 0) {
g_warning ("[repo stat] failed to create thread.\n");
return -1;
}
return 0;
}
static void
computation_finished (CcnetProcessor *processor,
int status,
char *message)
{
USE_PRIV;
g_ptr_array_free (priv->block_ids, TRUE);
priv->block_ids = NULL;
g_message ("Finished computing size of repo %s.\n", priv->repo_id);
/* Store repo size into database.
*/
if (seaf_monitor_set_repo_size (singleton_monitor,
priv->repo_id,
priv->repo_size,
priv->is_accurate,
priv->head) < 0) {
ccnet_processor_send_response (processor,
SC_DB_ERROR, SS_DB_ERROR,
NULL, 0);
ccnet_processor_done (processor, FALSE);
}
ccnet_processor_send_response (processor, SC_FINISHED, SS_FINISHED, NULL, 0);
ccnet_processor_done (processor, TRUE);
}

View File

@ -0,0 +1,30 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef SEAFILE_REPOSTAT_PROC_H
#define SEAFILE_REPOSTAT_PROC_H
#include <glib-object.h>
#include <ccnet/processor.h>
#define SEAFILE_TYPE_REPOSTAT_PROC (seafile_repostat_proc_get_type ())
#define SEAFILE_REPOSTAT_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAFILE_TYPE_REPOSTAT_PROC, SeafileRepostatProc))
#define SEAFILE_IS_REPOSTAT_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAFILE_TYPE_REPOSTAT_PROC))
#define SEAFILE_REPOSTAT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SEAFILE_TYPE_REPOSTAT_PROC, SeafileRepostatProcClass))
#define IS_SEAFILE_REPOSTAT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SEAFILE_TYPE_REPOSTAT_PROC))
#define SEAFILE_REPOSTAT_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SEAFILE_TYPE_REPOSTAT_PROC, SeafileRepostatProcClass))
typedef struct _SeafileRepostatProc SeafileRepostatProc;
typedef struct _SeafileRepostatProcClass SeafileRepostatProcClass;
struct _SeafileRepostatProc {
CcnetProcessor parent_instance;
};
struct _SeafileRepostatProcClass {
CcnetProcessorClass parent_class;
};
GType seafile_repostat_proc_get_type ();
#endif

823
monitor/repo-mgr.c Normal file
View File

@ -0,0 +1,823 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include <glib/gstdio.h>
#include <ccnet.h>
#include "utils.h"
#include "avl/avl.h"
#include "log.h"
#include "seafile-session.h"
#include "seafile-config.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#include "repo-mgr.h"
#include "fs-mgr.h"
#include "seafile-error.h"
#include "gc.h"
#include "seafile-crypt.h"
#include "index/index.h"
#include "index/cache-tree.h"
#include "unpack-trees.h"
#include "diff-simple.h"
#include "seaf-db.h"
#define INDEX_DIR "index"
struct _SeafRepoManagerPriv {
avl_tree_t *repo_tree;
pthread_rwlock_t lock;
};
static SeafRepo *
load_repo (SeafRepoManager *manager, const char *repo_id);
static int create_db_tables_if_not_exist (SeafRepoManager *mgr);
gboolean
is_repo_id_valid (const char *id)
{
if (!id)
return FALSE;
return is_uuid_valid (id);
}
SeafRepo*
seaf_repo_new (const char *id, const char *name, const char *desc)
{
SeafRepo* repo;
/* valid check */
repo = g_new0 (SeafRepo, 1);
memcpy (repo->id, id, 36);
repo->id[36] = '\0';
repo->name = g_strdup(name);
repo->desc = g_strdup(desc);
repo->ref_cnt = 1;
return repo;
}
void
seaf_repo_free (SeafRepo *repo)
{
if (repo->name) g_free (repo->name);
if (repo->desc) g_free (repo->desc);
if (repo->category) g_free (repo->category);
if (repo->head) seaf_branch_unref (repo->head);
g_free (repo);
}
void
seaf_repo_ref (SeafRepo *repo)
{
g_atomic_int_inc (&repo->ref_cnt);
}
void
seaf_repo_unref (SeafRepo *repo)
{
if (g_atomic_int_dec_and_test (&repo->ref_cnt))
seaf_repo_free (repo);
}
static void
set_head_common (SeafRepo *repo, SeafBranch *branch, SeafCommit *commit)
{
if (repo->head)
seaf_branch_unref (repo->head);
repo->head = branch;
seaf_branch_ref(branch);
}
void
seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit)
{
repo->name = g_strdup (commit->repo_name);
repo->desc = g_strdup (commit->repo_desc);
repo->encrypted = commit->encrypted;
if (repo->encrypted) {
repo->enc_version = commit->enc_version;
if (repo->enc_version >= 1)
memcpy (repo->magic, commit->magic, 33);
}
repo->no_local_history = commit->no_local_history;
}
void
seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit)
{
commit->repo_name = g_strdup (repo->name);
commit->repo_desc = g_strdup (repo->desc);
commit->encrypted = repo->encrypted;
if (commit->encrypted) {
commit->enc_version = repo->enc_version;
if (commit->enc_version >= 1)
commit->magic = g_strdup (repo->magic);
}
commit->no_local_history = repo->no_local_history;
}
static gboolean
collect_commit (SeafCommit *commit, void *vlist, gboolean *stop)
{
GList **commits = vlist;
/* The traverse function will unref the commit, so we need to ref it.
*/
seaf_commit_ref (commit);
*commits = g_list_prepend (*commits, commit);
return TRUE;
}
GList *
seaf_repo_get_commits (SeafRepo *repo)
{
GList *branches;
GList *ptr;
SeafBranch *branch;
GList *commits = NULL;
branches = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo->id);
if (branches == NULL) {
g_warning ("Failed to get branch list of repo %s.\n", repo->id);
return NULL;
}
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
branch = ptr->data;
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
branch->commit_id,
collect_commit,
&commits);
if (!res) {
for (ptr = commits; ptr != NULL; ptr = ptr->next)
seaf_commit_unref ((SeafCommit *)(ptr->data));
g_list_free (commits);
goto out;
}
}
commits = g_list_reverse (commits);
out:
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
seaf_branch_unref ((SeafBranch *)ptr->data);
}
return commits;
}
static int
compare_repo (const SeafRepo *srepo, const SeafRepo *trepo)
{
return g_strcmp0 (srepo->id, trepo->id);
}
SeafRepoManager*
seaf_repo_manager_new (SeafileSession *seaf)
{
SeafRepoManager *mgr = g_new0 (SeafRepoManager, 1);
mgr->priv = g_new0 (SeafRepoManagerPriv, 1);
mgr->seaf = seaf;
mgr->priv->repo_tree = avl_alloc_tree ((avl_compare_t)compare_repo,
NULL);
pthread_rwlock_init (&mgr->priv->lock, NULL);
return mgr;
}
int
seaf_repo_manager_init (SeafRepoManager *mgr)
{
/* On the server, we load repos into memory on-demand, because
* there are too many repos.
*/
if (create_db_tables_if_not_exist (mgr) < 0) {
g_warning ("[repo mgr] failed to create tables.\n");
return -1;
}
return 0;
}
int
seaf_repo_manager_start (SeafRepoManager *mgr)
{
return 0;
}
static gboolean
repo_exists_in_db (SeafDB *db, const char *id)
{
char sql[256];
snprintf (sql, sizeof(sql), "SELECT repo_id FROM Repo WHERE repo_id = '%s'",
id);
return seaf_db_check_for_existence (db, sql);
}
SeafRepo*
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id)
{
SeafRepo repo;
int len = strlen(id);
if (len >= 37)
return NULL;
memcpy (repo.id, id, len + 1);
#if 0
if (pthread_rwlock_rdlock (&manager->priv->lock) < 0) {
g_warning ("[repo mgr] failed to lock repo cache.\n");
return NULL;
}
avl_node_t *res = avl_search (manager->priv->repo_tree, &repo);
pthread_rwlock_unlock (&manager->priv->lock);
if (res) {
seaf_repo_ref ((SeafRepo *)(res->item));
return res->item;
}
#endif
if (repo_exists_in_db (manager->seaf->db, id)) {
SeafRepo *ret = load_repo (manager, id);
if (!ret)
return NULL;
/* seaf_repo_ref (ret); */
return ret;
}
return NULL;
}
SeafRepo*
seaf_repo_manager_get_repo_prefix (SeafRepoManager *manager, const gchar *id)
{
avl_node_t *node;
SeafRepo repo, *result;
int len = strlen(id);
if (len >= 37)
return NULL;
memcpy (repo.id, id, len + 1);
avl_search_closest (manager->priv->repo_tree, &repo, &node);
if (node != NULL) {
result = node->item;
if (strncmp (id, result->id, len) == 0)
return node->item;
}
return NULL;
}
gboolean
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id)
{
SeafRepo repo;
memcpy (repo.id, id, 37);
#if 0
if (pthread_rwlock_rdlock (&manager->priv->lock) < 0) {
g_warning ("[repo mgr] failed to lock repo cache.\n");
return FALSE;
}
avl_node_t *res = avl_search (manager->priv->repo_tree, &repo);
pthread_rwlock_unlock (&manager->priv->lock);
if (res)
return TRUE;
#endif
return repo_exists_in_db (manager->seaf->db, id);
}
gboolean
seaf_repo_manager_repo_exists_prefix (SeafRepoManager *manager, const gchar *id)
{
avl_node_t *node;
SeafRepo repo;
memcpy (repo.id, id, 37);
avl_search_closest (manager->priv->repo_tree, &repo, &node);
if (node != NULL)
return TRUE;
return FALSE;
}
static void
load_repo_commit (SeafRepoManager *manager,
SeafRepo *repo,
SeafBranch *branch)
{
SeafCommit *commit;
commit = seaf_commit_manager_get_commit (manager->seaf->commit_mgr,
branch->commit_id);
if (!commit) {
g_warning ("Commit %s is missing\n", branch->commit_id);
repo->is_corrupted = TRUE;
return;
}
set_head_common (repo, branch, commit);
seaf_repo_from_commit (repo, commit);
seaf_commit_unref (commit);
}
static gboolean
load_branch_cb (SeafDBRow *row, void *vrepo)
{
SeafRepo *repo = vrepo;
SeafRepoManager *manager = repo->manager;
const char *branch_name = seaf_db_row_get_column_text (row, 0);
SeafBranch *branch =
seaf_branch_manager_get_branch (manager->seaf->branch_mgr,
repo->id, branch_name);
if (branch == NULL) {
g_warning ("Broken branch name for repo %s\n", repo->id);
repo->is_corrupted = TRUE;
return FALSE;
}
load_repo_commit (manager, repo, branch);
seaf_branch_unref (branch);
/* Only one result. */
return FALSE;
}
static SeafRepo *
load_repo (SeafRepoManager *manager, const char *repo_id)
{
char sql[256];
SeafRepo *repo = seaf_repo_new(repo_id, NULL, NULL);
if (!repo) {
g_warning ("[repo mgr] failed to alloc repo.\n");
return NULL;
}
repo->manager = manager;
snprintf(sql, 256, "SELECT branch_name FROM RepoHead WHERE repo_id='%s'",
repo->id);
if (seaf_db_foreach_selected_row (seaf->db, sql, load_branch_cb, repo) < 0) {
g_warning ("Error read branch for repo %s.\n", repo->id);
seaf_repo_unref (repo);
return NULL;
}
if (repo->is_corrupted) {
seaf_repo_free (repo);
return NULL;
}
#if 0
if (pthread_rwlock_wrlock (&manager->priv->lock) < 0) {
g_warning ("[repo mgr] failed to lock repo cache.\n");
seaf_repo_free (repo);
return NULL;
}
avl_insert (manager->priv->repo_tree, repo);
/* Don't need to increase ref count, since the ref count of
* a new repo object is already 1.
*/
pthread_rwlock_unlock (&manager->priv->lock);
#endif
return repo;
}
static int
create_db_tables_if_not_exist (SeafRepoManager *mgr)
{
SeafDB *db = mgr->seaf->db;
char *sql = "CREATE TABLE IF NOT EXISTS Repo (repo_id CHAR(37) PRIMARY KEY)";
if (seaf_db_query (db, sql) < 0)
return -1;
int db_type = seaf_db_type (db);
if (db_type == SEAF_DB_TYPE_MYSQL) {
sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
"repo_id CHAR(37) PRIMARY KEY, "
"owner_id VARCHAR(255),"
"INDEX (owner_id))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE RepoGroup (repo_id CHAR(37), group_id INTEGER, "
"user_name VARCHAR(255), permission CHAR(15), "
"UNIQUE INDEX (group_id, repo_id))";
if (seaf_db_query (db, sql) < 0)
return -1;
} else if (db_type == SEAF_DB_TYPE_SQLITE) {
sql = "CREATE TABLE IF NOT EXISTS RepoOwner ("
"repo_id CHAR(37) PRIMARY KEY, "
"owner_id TEXT)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE INDEX IF NOT EXISTS OwnerIndex ON RepoOwner (owner_id)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS RepoGroup (repo_id CHAR(37), "
"group_id INTEGER, user_name TEXT, permission CHAR(15))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupid_repoid_indx on "
"RepoGroup (group_id, repo_id)";
if (seaf_db_query (db, sql) < 0)
return -1;
}
sql = "CREATE TABLE IF NOT EXISTS RepoHead ("
"repo_id CHAR(37) PRIMARY KEY, branch_name VARCHAR(10))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS RepoToken ("
"repo_id CHAR(37) PRIMARY KEY, token VARCHAR(65))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS PublicRepo (repo_id CHAR(37) PRIMARY KEY)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS WebAP (repo_id CHAR(37) PRIMARY KEY, "
"access_property CHAR(10))";
if (seaf_db_query (db, sql) < 0)
return -1;
return 0;
}
int
seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr,
const char *repo_id,
const char *email)
{
char sql[256];
snprintf (sql, sizeof(sql), "REPLACE INTO RepoOwner VALUES ('%s', '%s')",
repo_id, email);
if (seaf_db_query (mgr->seaf->db, sql) < 0)
return -1;
return 0;
}
static gboolean
get_owner (SeafDBRow *row, void *data)
{
char **owner_id = data;
*owner_id = g_strdup(seaf_db_row_get_column_text (row, 0));
/* There should be only one result. */
return FALSE;
}
char *
seaf_repo_manager_get_repo_owner (SeafRepoManager *mgr,
const char *repo_id)
{
char sql[256];
char *ret = NULL;
snprintf (sql, sizeof(sql),
"SELECT owner_id FROM RepoOwner WHERE repo_id='%s'",
repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
get_owner, &ret) < 0) {
g_warning ("Failed to get owner id for repo %s.\n", repo_id);
return NULL;
}
return ret;
}
static gboolean
collect_repos (SeafDBRow *row, void *data)
{
GList **p_repos = data;
const char *repo_id;
SeafRepo *repo;
repo_id = seaf_db_row_get_column_text (row, 0);
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
if (!repo) {
/* Continue to collect the remaining repos. */
return TRUE;
}
*p_repos = g_list_prepend (*p_repos, repo);
return TRUE;
}
GList *
seaf_repo_manager_get_repos_by_owner (SeafRepoManager *mgr,
const char *email)
{
GList *ret = NULL;
char sql[256];
snprintf (sql, 256, "SELECT repo_id FROM RepoOwner WHERE owner_id='%s'",
email);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
collect_repos, &ret) < 0)
return NULL;
return g_list_reverse (ret);
}
static gboolean
collect_repo_id (SeafDBRow *row, void *data)
{
GList **p_ids = data;
const char *repo_id;
repo_id = seaf_db_row_get_column_text (row, 0);
*p_ids = g_list_prepend (*p_ids, g_strdup(repo_id));
return TRUE;
}
GList *
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr)
{
GList *ret = NULL;
char sql[256];
snprintf (sql, 256, "SELECT repo_id FROM Repo");
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
collect_repo_id, &ret) < 0)
return NULL;
return ret;
}
GList *
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit)
{
GList *ret = NULL;
char sql[256];
if (start == -1 && limit == -1)
snprintf (sql, 256, "SELECT repo_id FROM Repo");
else
snprintf (sql, 256, "SELECT repo_id FROM Repo LIMIT %d, %d", start, limit);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
collect_repos, &ret) < 0)
return NULL;
return g_list_reverse (ret);
}
static gboolean
get_repo_size (SeafDBRow *row, void *vsize)
{
gint64 *psize = vsize;
*psize = seaf_db_row_get_column_int64 (row, 0);
return FALSE;
}
gint64
seaf_repo_manager_get_repo_size (SeafRepoManager *mgr, const char *repo_id)
{
gint64 size = 0;
char sql[256];
snprintf (sql, sizeof(sql), "SELECT size FROM RepoSize WHERE repo_id='%s'",
repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
get_repo_size, &size) < 0)
return -1;
return size;
}
int
seaf_repo_manager_set_access_property (SeafRepoManager *mgr, const char *repo_id,
const char *ap)
{
char sql[256];
if (seaf_repo_manager_query_access_property (mgr, repo_id) == NULL) {
snprintf (sql, sizeof(sql), "INSERT INTO WebAP VALUES ('%s', '%s')",
repo_id, ap);
} else {
snprintf (sql, sizeof(sql), "UPDATE WebAP SET access_property='%s' "
"WHERE repo_id='%s'", ap, repo_id);
}
if (seaf_db_query (mgr->seaf->db, sql) < 0) {
g_warning ("DB error when set access property for repo %s, %s.\n", repo_id, ap);
return -1;
}
return 0;
}
static gboolean
get_ap (SeafDBRow *row, void *data)
{
char **ap = data;
*ap = g_strdup (seaf_db_row_get_column_text (row, 0));
return FALSE;
}
char *
seaf_repo_manager_query_access_property (SeafRepoManager *mgr, const char *repo_id)
{
char sql[256];
char *ret = NULL;
snprintf (sql, sizeof(sql), "SELECT access_property FROM WebAP WHERE repo_id='%s'",
repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql, get_ap, &ret) < 0) {
g_warning ("DB error when get access property for repo %s.\n", repo_id);
return NULL;
}
return ret;
}
int
seaf_repo_manager_share_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
const char *permission,
GError **error)
{
char sql[512];
snprintf (sql, sizeof(sql), "INSERT INTO RepoGroup VALUES ('%s', %d, '%s', '%s')",
repo_id, group_id, user_name, permission);
if (seaf_db_query (mgr->seaf->db, sql) < 0)
return -1;
return 0;
}
int
seaf_repo_manager_unshare_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
GError **error)
{
char sql[512];
snprintf (sql, sizeof(sql), "DELETE FROM RepoGroup WHERE repo_id='%s' "
"AND group_id=%d", repo_id, group_id);
return seaf_db_query (mgr->seaf->db, sql);
}
static gboolean
get_group_repoids_cb (SeafDBRow *row, void *data)
{
GList **p_list = data;
char *repo_id = g_strdup ((const char *)seaf_db_row_get_column_text (row, 0));
*p_list = g_list_prepend (*p_list, repo_id);
return TRUE;
}
GList *
seaf_repo_manager_get_group_repoids (SeafRepoManager *mgr,
int group_id,
GError **error)
{
char sql[512];
GList *repo_ids = NULL;
snprintf (sql, sizeof(sql), "SELECT repo_id FROM RepoGroup "
"WHERE group_id = %d", group_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql, get_group_repoids_cb,
&repo_ids) < 0)
return NULL;
return g_list_reverse (repo_ids);
}
static gboolean
get_group_repos_cb (SeafDBRow *row, void *data)
{
GList **p_list = data;
SeafileRepoGroup *repo_group = NULL;
char *repo_id = g_strdup ((const char *)seaf_db_row_get_column_text (row, 0));
int group_id = seaf_db_row_get_column_int (row, 1);
char *user_name = g_strdup ((const char *)seaf_db_row_get_column_text (row, 2));
repo_group = g_object_new (SEAFILE_TYPE_REPO_GROUP,
"repo_id", repo_id,
"group_id", group_id,
"user_name", user_name,
NULL);
if (repo_group != NULL) {
/* g_object_ref (repo_group); */
*p_list = g_list_prepend (*p_list, repo_group);
}
return TRUE;
}
GList *
seaf_repo_manager_get_group_my_share_repos (SeafRepoManager *mgr,
const char *username,
GError **error)
{
char sql[512];
GList *repos = NULL;
snprintf (sql, sizeof(sql), "SELECT repo_id, group_id, user_name "
"FROM RepoGroup WHERE user_name = '%s'", username);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql, get_group_repos_cb,
&repos) < 0)
return NULL;
return g_list_reverse (repos);
}
static gboolean
get_repo_share_from (SeafDBRow *row, void *data)
{
char **share_from = data;
*share_from = g_strdup (seaf_db_row_get_column_text (row, 0));
/* There should be only one result. */
return FALSE;
}
char *
seaf_repo_manager_get_repo_share_from (SeafRepoManager *mgr,
const char *repo_id,
GError **error)
{
char sql[512];
char *ret = NULL;
snprintf (sql, sizeof(sql), "SELECT user_name FROM RepoGroup "
"WHERE repo_id = '%s'", repo_id);
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
get_repo_share_from, &ret) < 0) {
g_warning ("DB error when get repo share from for repo %s.\n",
repo_id);
return NULL;
}
return ret;
}
int
seaf_repo_manager_remove_repo_group (SeafRepoManager *mgr,
int group_id,
const char *user_name,
GError **error)
{
char sql[512];
if (!user_name) {
snprintf (sql, sizeof(sql), "DELETE FROM RepoGroup WHERE group_id=%d",
group_id);
} else {
snprintf (sql, sizeof(sql), "DELETE FROM RepoGroup WHERE group_id=%d AND "
"user_name = '%s'", group_id, user_name);
}
return seaf_db_query (mgr->seaf->db, sql);
}

162
monitor/repo-mgr.h Normal file
View File

@ -0,0 +1,162 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef SEAF_REPO_MGR_H
#define SEAF_REPO_MGR_H
#include <pthread.h>
#include "seafile-object.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#define REPO_AUTO_SYNC "auto-sync"
#define REPO_AUTO_FETCH "auto-fetch"
#define REPO_AUTO_UPLOAD "auto-upload"
#define REPO_AUTO_MERGE "auto-merge"
#define REPO_AUTO_COMMIT "auto-commit"
#define REPO_RELAY_ID "relay-id"
#define REPO_NET_BROWSABLE "net-browsable"
#define REPO_DOUBLE_SYNC "double-sync"
#define REPO_REMOTE_HEAD "remote-head"
#define REPO_ENCRYPTED 0x1
struct _SeafRepoManager;
typedef struct _SeafRepo SeafRepo;
struct _SeafRepo {
struct _SeafRepoManager *manager;
gchar id[37];
gchar *name;
gchar *desc;
gchar *category; /* not used yet */
gboolean encrypted;
int enc_version;
gchar magic[33]; /* hash(repo_id + passwd), key stretched. */
gboolean no_local_history;
SeafBranch *head;
gboolean is_corrupted;
gboolean delete_pending;
int ref_cnt;
};
gboolean is_repo_id_valid (const char *id);
SeafRepo*
seaf_repo_new (const char *id, const char *name, const char *desc);
void
seaf_repo_free (SeafRepo *repo);
void
seaf_repo_ref (SeafRepo *repo);
void
seaf_repo_unref (SeafRepo *repo);
typedef struct _SeafRepoManager SeafRepoManager;
typedef struct _SeafRepoManagerPriv SeafRepoManagerPriv;
struct _SeafRepoManager {
struct _SeafileSession *seaf;
SeafRepoManagerPriv *priv;
};
SeafRepoManager*
seaf_repo_manager_new (struct _SeafileSession *seaf);
int
seaf_repo_manager_init (SeafRepoManager *mgr);
int
seaf_repo_manager_start (SeafRepoManager *mgr);
int
seaf_repo_manager_add_repo (SeafRepoManager *mgr, SeafRepo *repo);
int
seaf_repo_manager_del_repo (SeafRepoManager *mgr, SeafRepo *repo);
SeafRepo*
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id);
SeafRepo*
seaf_repo_manager_get_repo_prefix (SeafRepoManager *manager, const gchar *id);
gboolean
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id);
gboolean
seaf_repo_manager_repo_exists_prefix (SeafRepoManager *manager, const gchar *id);
GList*
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit);
int
seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr,
const char *repo_id,
const char *email);
char *
seaf_repo_manager_get_repo_owner (SeafRepoManager *mgr,
const char *repo_id);
/* TODO: add start and limit. */
/* Get repos owned by this user.
*/
GList *
seaf_repo_manager_get_repos_by_owner (SeafRepoManager *mgr,
const char *email);
GList *
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr);
gint64
seaf_repo_manager_get_repo_size (SeafRepoManager *mgr, const char *repo_id);
int
seaf_repo_manager_set_access_property (SeafRepoManager *mgr, const char *repo_id,
const char *ap);
char *
seaf_repo_manager_query_access_property (SeafRepoManager *mgr, const char *repo_id);
int
seaf_repo_manager_share_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
const char *permission,
GError **error);
int
seaf_repo_manager_unshare_repo (SeafRepoManager *mgr,
const char *repo_id,
int group_id,
const char *user_name,
GError **error);
GList *
seaf_repo_manager_get_group_repoids (SeafRepoManager *mgr,
int group_id,
GError **error);
GList *
seaf_repo_manager_get_group_my_share_repos (SeafRepoManager *mgr,
const char *username,
GError **error);
char *
seaf_repo_manager_get_repo_share_from (SeafRepoManager *mgr,
const char *repo_id,
GError **error);
int
seaf_repo_manager_remove_repo_group (SeafRepoManager *mgr,
int group_id,
const char *user_name,
GError **error);
#endif

194
monitor/scheduler.c Normal file
View File

@ -0,0 +1,194 @@
#include "common.h"
#include <ccnet/timer.h>
#include "seafile-session.h"
#include "scheduler.h"
typedef struct SchedulerPriv {
GQueue *repo_size_job_queue;
int n_running_repo_size_jobs;
CcnetTimer *sched_timer;
} SchedulerPriv;
typedef struct RepoSizeJob {
Scheduler *sched;
char repo_id[37];
} RepoSizeJob;
#define SCHEDULER_INTV 10000 /* 10s */
#define CONCURRENT_JOBS 5
static int
schedule_pulse (void *vscheduler);
static void*
compute_repo_size (void *vjob);
static void
compute_repo_size_done (void *vjob);
Scheduler *
scheduler_new (SeafileSession *session)
{
Scheduler *sched = g_new0 (Scheduler, 1);
if (!sched)
return NULL;
sched->priv = g_new0 (SchedulerPriv, 1);
if (!sched->priv) {
g_free (sched);
return NULL;
}
sched->seaf = session;
return sched;
}
static int
create_repo_stat_tables (SeafileSession *session)
{
SeafDB *db = session->db;
char *sql = "CREATE TABLE IF NOT EXISTS RepoSize ("
"repo_id CHAR(37) PRIMARY KEY,"
"size BIGINT UNSIGNED)";
if (seaf_db_query (db, sql) < 0)
return -1;
return 0;
}
int
scheduler_init (Scheduler *scheduler)
{
if (create_repo_stat_tables (scheduler->seaf) < 0) {
g_warning ("[scheduler] failed to create stat tables.\n");
return -1;
}
scheduler->priv->repo_size_job_queue = g_queue_new ();
scheduler->priv->sched_timer = ccnet_timer_new (schedule_pulse,
scheduler,
SCHEDULER_INTV);
return 0;
}
void
schedule_repo_size_computation (Scheduler *scheduler, const char *repo_id)
{
RepoSizeJob *job = g_new0(RepoSizeJob, 1);
job->sched = scheduler;
memcpy (job->repo_id, repo_id, 37);
g_queue_push_tail (scheduler->priv->repo_size_job_queue, job);
}
static int
schedule_pulse (void *vscheduler)
{
Scheduler *sched = vscheduler;
RepoSizeJob *job;
while (sched->priv->n_running_repo_size_jobs < CONCURRENT_JOBS) {
job = (RepoSizeJob *)g_queue_pop_head (sched->priv->repo_size_job_queue);
if (!job)
break;
int ret = ccnet_job_manager_schedule_job (sched->seaf->job_mgr,
compute_repo_size,
compute_repo_size_done,
job);
if (ret < 0) {
g_warning ("[scheduler] failed to start compute job.\n");
g_queue_push_head (sched->priv->repo_size_job_queue, job);
break;
}
++(sched->priv->n_running_repo_size_jobs);
}
return 1;
}
static gboolean
load_blocklist (SeafCommit *commit, void *data, gboolean *stop)
{
BlockList *bl = data;
if (seaf_fs_manager_populate_blocklist (seaf->fs_mgr, commit->root_id, bl) < 0)
return FALSE;
return TRUE;
}
static int
set_repo_size (SeafDB *db, const char *repo_id, guint64 size)
{
char sql[256];
snprintf (sql, sizeof(sql), "REPLACE INTO RepoSize VALUES ('%s', %"G_GUINT64_FORMAT")",
repo_id, size);
if (seaf_db_query (db, sql) < 0)
return -1;
return 0;
}
static void*
compute_repo_size (void *vjob)
{
RepoSizeJob *job = vjob;
Scheduler *sched = job->sched;
SeafRepo *repo;
BlockList *bl;
char *block_id;
BlockMetadata *bmd;
guint64 size = 0;
repo = seaf_repo_manager_get_repo (sched->seaf->repo_mgr, job->repo_id);
if (!repo) {
g_warning ("[scheduler] failed to get repo %s.\n", job->repo_id);
return vjob;
}
/* Load block list first so that we don't need to count duplicate blocks.
*/
bl = block_list_new ();
if (!seaf_commit_manager_traverse_commit_tree (sched->seaf->commit_mgr,
repo->head->commit_id,
load_blocklist,
bl)) {
seaf_repo_unref (repo);
block_list_free (bl);
return vjob;
}
seaf_repo_unref (repo);
int i;
for (i = 0; i < bl->n_blocks; ++i) {
block_id = g_ptr_array_index (bl->block_ids, i);
bmd = seaf_block_manager_stat_block (sched->seaf->block_mgr, block_id);
if (bmd) {
size += bmd->size;
g_free (bmd);
}
}
block_list_free (bl);
if (set_repo_size (sched->seaf->db, job->repo_id, size) < 0) {
g_warning ("[scheduler] failed to store repo size %s.\n", job->repo_id);
return vjob;
}
return vjob;
}
static void
compute_repo_size_done (void *vjob)
{
RepoSizeJob *job = vjob;
--(job->sched->priv->n_running_repo_size_jobs);
g_free (job);
}

23
monitor/scheduler.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef SCHEDULER_H
#define SCHEDULER_H
struct _SeafileSession;
struct SchedulerPriv;
typedef struct Scheduler {
struct _SeafileSession *seaf;
struct SchedulerPriv *priv;
} Scheduler;
Scheduler *
scheduler_new (struct _SeafileSession *session);
int
scheduler_init (Scheduler *scheduler);
void
schedule_repo_size_computation (Scheduler *scheduler, const char *repo_id);
#endif

230
monitor/seaf-mon.c Normal file
View File

@ -0,0 +1,230 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <glib.h>
#include <glib-object.h>
#include <ccnet.h>
#include <searpc-server.h>
#include "seafile-session.h"
#include "monitor-rpc.h"
#include "rpcserver-proc.h"
#include "log.h"
#include "utils.h"
/* #include "processors/heartbeat-proc.h" */
/* #include "processors/repostat-proc.h" */
SeafileSession *seaf;
char *pidfile = NULL;
static const char *short_options = "hvc:d:l:fg:G:P:";
static struct option long_options[] = {
{ "help", no_argument, NULL, 'h', },
{ "version", no_argument, NULL, 'v', },
{ "config-file", required_argument, NULL, 'c' },
{ "seafile-dir", required_argument, NULL, 'd' },
{ "log", required_argument, NULL, 'l' },
{ "foreground", no_argument, NULL, 'f' },
{ "ccnet-debug-level", required_argument, NULL, 'g' },
{ "monitor-debug-level", required_argument, NULL, 'G' },
{ "pidfile", required_argument, NULL, 'P' },
{ NULL, 0, NULL, 0, },
};
static void usage ()
{
fprintf (stderr, "usage: seaf-mon -d seafile_dir [-c config_dir]\n");
}
static void register_processors (CcnetClient *client)
{
}
#include <searpc.h>
#include "searpc-signature.h"
#include "searpc-marshal.h"
static void start_rpc_service (CcnetClient *client)
{
searpc_server_init (register_marshals);
searpc_create_service ("monitor-rpcserver");
ccnet_register_service (client, "monitor-rpcserver", "rpc-inner",
CCNET_TYPE_RPCSERVER_PROC, NULL);
searpc_server_register_function ("monitor-rpcserver",
monitor_compute_repo_size,
"compute_repo_size",
searpc_signature_int__string());
}
static void
set_signal_handlers (SeafileSession *session)
{
signal (SIGPIPE, SIG_IGN);
}
static void
remove_pidfile (const char *pidfile)
{
if (pidfile) {
g_unlink (pidfile);
}
}
static int
write_pidfile (const char *pidfile_path)
{
if (!pidfile_path)
return -1;
pid_t pid = getpid();
FILE *pidfile = fopen(pidfile_path, "w");
if (!pidfile) {
seaf_warning ("Failed to fopen() pidfile %s: %s\n",
pidfile_path, strerror(errno));
return -1;
}
char buf[32];
snprintf (buf, sizeof(buf), "%d\n", pid);
if (fputs(buf, pidfile) < 0) {
seaf_warning ("Failed to write pidfile %s: %s\n",
pidfile_path, strerror(errno));
return -1;
}
fflush (pidfile);
fclose (pidfile);
return 0;
}
static void
on_seaf_mon_exit(void)
{
if (pidfile)
remove_pidfile (pidfile);
}
int
main (int argc, char **argv)
{
int c;
char *config_dir = DEFAULT_CONFIG_DIR;
char *seafile_dir = NULL;
char *logfile = NULL;
int daemon_mode = 1;
CcnetClient *client;
char *ccnet_debug_level_str = "info";
char *monitor_debug_level_str = "debug";
while ((c = getopt_long (argc, argv, short_options,
long_options, NULL)) != EOF)
{
switch (c) {
case 'h':
exit (1);
break;
case 'v':
exit (1);
break;
case 'c':
config_dir = optarg;
break;
case 'd':
seafile_dir = g_strdup(optarg);
break;
case 'f':
daemon_mode = 0;
break;
case 'l':
logfile = g_strdup(optarg);
break;
case 'g':
ccnet_debug_level_str = optarg;
break;
case 'G':
monitor_debug_level_str = optarg;
break;
case 'P':
pidfile = optarg;
break;
default:
usage ();
exit (1);
}
}
argc -= optind;
argv += optind;
if (daemon_mode)
daemon (1, 0);
g_type_init ();
client = ccnet_init (config_dir);
if (!client)
exit (1);
register_processors (client);
start_rpc_service (client);
if (seafile_dir == NULL) {
usage ();
exit (1);
}
if (logfile == NULL)
logfile = g_build_filename (seafile_dir, "monitor.log", NULL);
seaf = seafile_session_new (seafile_dir, client);
if (!seaf) {
fprintf (stderr, "Failed to create seafile monitor.\n");
exit (1);
}
if (seafile_session_init (seaf) < 0) {
fprintf (stderr, "Failed to init seafile monitor.\n");
exit (1);
}
if (seafile_log_init (logfile, ccnet_debug_level_str,
monitor_debug_level_str) < 0) {
fprintf (stderr, "Failed to init log.\n");
exit (1);
}
g_free (seafile_dir);
g_free (logfile);
set_signal_handlers (seaf);
seafile_session_start (seaf);
if (pidfile) {
if (write_pidfile (pidfile) < 0) {
ccnet_message ("Failed to write pidfile\n");
return -1;
}
}
atexit (on_seaf_mon_exit);
ccnet_main (client);
return 0;
}

158
monitor/seafile-session.c Normal file
View File

@ -0,0 +1,158 @@
#include "common.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ccnet.h>
#include <utils.h>
#include "seafile-session.h"
#include "seafile-config.h"
#include "seaf-utils.h"
#define REFRESH_INTV 86400 /* 24 hours */
static int
refresh_all_repo_sizes (void *vsession);
SeafileSession *
seafile_session_new(const char *seafile_dir,
CcnetClient *ccnet_session)
{
char *abs_seafile_dir;
char *tmp_file_dir;
char *config_file_path;
struct stat st;
GKeyFile *config;
SeafileSession *session;
if (!ccnet_session)
return NULL;
abs_seafile_dir = ccnet_expand_path (seafile_dir);
tmp_file_dir = g_build_filename (abs_seafile_dir, "tmpfiles", NULL);
config_file_path = g_build_filename (abs_seafile_dir, "seafile.conf", NULL);
if (g_lstat(abs_seafile_dir, &st) < 0 || !S_ISDIR(st.st_mode)) {
g_warning ("Seafile data dir %s does not exist and is unable to create\n",
abs_seafile_dir);
goto onerror;
}
if (g_lstat(tmp_file_dir, &st) < 0 || !S_ISDIR(st.st_mode)) {
g_warning ("Seafile tmp dir %s does not exist and is unable to create\n",
tmp_file_dir);
goto onerror;
}
GError *error = NULL;
config = g_key_file_new ();
if (!g_key_file_load_from_file (config, config_file_path,
G_KEY_FILE_NONE, &error)) {
g_warning ("Failed to load config file.\n");
g_key_file_free (config);
goto onerror;
}
session = g_new0(SeafileSession, 1);
session->seaf_dir = abs_seafile_dir;
session->tmp_file_dir = tmp_file_dir;
session->session = ccnet_session;
session->config = config;
if (load_database_config (session) < 0) {
g_warning ("Failed to load database config.\n");
goto onerror;
}
session->fs_mgr = seaf_fs_manager_new (session, abs_seafile_dir);
if (!session->fs_mgr)
goto onerror;
session->block_mgr = seaf_block_manager_new (session, abs_seafile_dir);
if (!session->block_mgr)
goto onerror;
session->commit_mgr = seaf_commit_manager_new (session);
if (!session->commit_mgr)
goto onerror;
session->repo_mgr = seaf_repo_manager_new (session);
if (!session->repo_mgr)
goto onerror;
session->branch_mgr = seaf_branch_manager_new (session);
if (!session->branch_mgr)
goto onerror;
session->job_mgr = ccnet_job_manager_new ();
session->scheduler = scheduler_new (session);
session->mq_mgr = seaf_mq_manager_new (session);
if (!session->mq_mgr)
goto onerror;
return session;
onerror:
free (abs_seafile_dir);
g_free (tmp_file_dir);
g_free (config_file_path);
g_free (session);
return NULL;
}
int
seafile_session_init (SeafileSession *session)
{
seaf_commit_manager_init (session->commit_mgr);
seaf_fs_manager_init (session->fs_mgr);
seaf_branch_manager_init (session->branch_mgr);
seaf_repo_manager_init (session->repo_mgr);
if (scheduler_init (session->scheduler) < 0)
return -1;
seaf_mq_manager_init (session->mq_mgr);
seaf_mq_manager_set_heartbeat_name (session->mq_mgr,
"seaf_mon.heartbeat");
return 0;
}
int
seafile_session_start (SeafileSession *session)
{
if (seaf_mq_manager_start (session->mq_mgr) < 0) {
g_error ("Failed to start mq manager.\n");
return -1;
}
/* refresh on restart. */
refresh_all_repo_sizes (session);
/* refresh every 24 hours. */
session->refresh_timer = ccnet_timer_new (refresh_all_repo_sizes,
session,
REFRESH_INTV * 1000);
return 0;
}
static int
refresh_all_repo_sizes (void *vsession)
{
SeafileSession *session = vsession;
GList *id_list;
GList *ptr;
char *repo_id;
id_list = seaf_repo_manager_get_repo_id_list (session->repo_mgr);
for (ptr = id_list; ptr != NULL; ptr = ptr->next) {
repo_id = ptr->data;
schedule_repo_size_computation (session->scheduler, repo_id);
g_free (repo_id);
}
g_list_free (id_list);
return 1;
}

60
monitor/seafile-session.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef SEAFILE_SESSION_H
#define SEAFILE_SESSION_H
#include <stdint.h>
#include <glib.h>
#include <ccnet/job-mgr.h>
#include <ccnet/timer.h>
#include "block-mgr.h"
#include "fs-mgr.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#include "repo-mgr.h"
#include "db.h"
#include "seaf-db.h"
#include "scheduler.h"
#include "mq-mgr.h"
struct _CcnetClient;
typedef struct _SeafileSession SeafileSession;
struct _SeafileSession {
struct _CcnetClient *session;
char *seaf_dir;
char *tmp_file_dir;
/* Config that's only loaded on start */
GKeyFile *config;
SeafDB *db;
SeafBlockManager *block_mgr;
SeafFSManager *fs_mgr;
SeafCommitManager *commit_mgr;
SeafBranchManager *branch_mgr;
SeafRepoManager *repo_mgr;
CcnetJobManager *job_mgr;
SeafMqManager *mq_mgr;
Scheduler *scheduler;
CcnetTimer *refresh_timer;
};
/* singleton monitor.
*/
extern SeafileSession *seaf;
SeafileSession *
seafile_session_new (const char *seafile_dir,
struct _CcnetClient *ccnet);
int
seafile_session_init (SeafileSession *session);
int
seafile_session_start (SeafileSession *session);
#endif

24
tests/Makefile.am Normal file
View File

@ -0,0 +1,24 @@
AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \
@GLIB2_CFLAGS@
check_PROGRAMS = test-seafile-fmt test-cdc test-index
test_seafile_fmt_SOURCES = test-seafile-fmt.c
test_seafile_fmt_CFLAGS = -I$(top_srcdir)/seafile/daemon \
-I$(top_srcdir)/lib @GLIB2_CFLAGS@
test_seafile_fmt_LDADD = $(top_builddir)/lib/libccnet.la -lssl -levent @GLIB2_LIBS@
test_cdc_SOURCES = test-cdc.c
test_cdc_CFLAGS = -I$(top_srcdir)/seafile/common/cdc @GLIB2_CFLAGS@
test_cdc_LDADD = $(top_builddir)/seafile/common/cdc/libcdc.la @GLIB2_LIBS@ -lcrypto
test_cdc_LDFLAGS = @STATIC_COMPILE@
test_index_SOURCES = test-index.c
test_index_CFLAGS = -I$(top_srcdir)/seafile/common/index
test_index_LDADD = $(top_builddir)/seafile/common/index/libindex.la -lcrypto
test_index_LDFLAGS = @STATIC_COMPILE@
TESTS =

4
tests/README Normal file
View File

@ -0,0 +1,4 @@
Test Cases
==========

66
tests/basic/README Normal file
View File

@ -0,0 +1,66 @@
Set up
======
Run setup.sh to set up peer relationship.
Start with four peers
* plt (conf1), freeplant (conf3): two end nodes
* server (conf2): default relay of plt and freeplant
* server2 (conf4): a chunk server
Roles
| | plt | freeplant | server | server2 |
|----------|----------|-----------|---------|---------|
|plt | | MyPeer | MyRelay | |
|freeplant | MyPeer | | MyRelay | |
|server | MyClient | MyClient | | MyClient|
|server2 | | | MyRelay | |
Test
====
One Chunk Server
-----------------
### Setup Relay
Start server
./seafile.sh 2
### Test work flow:
0. mkdir worktree
1. Start seaf-daemons
./seafile.sh 1 3
3. cd ../../../web and run run-seafileweb.sh to start web UI server
4. Test various operations on the web UI.
Two Chunk Server
----------------
### Setup Relay
./seafile.sh 2 4
./seafserv-tool -c conf2 add-server 93ae3e01eea6667cbdd03c4afde413ccd9f1eb43
### Test work flow:
0. mkdir worktree
1. Start seaf-daemons
./seafile.sh 1 3
3. cd ../../../web and run run-seafileweb.sh to start web UI server
4. Test various operations on the web UI.
Clean up
========
Run clean.sh to clear all configuration and data related to seafile.
Run teardown.sh to clear all configurations and data, including ccnet config.
Run 'git checkout HEAD conf{1,2,3,4}' to clean all changes to config dirs.

11
tests/basic/clean.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
CLEANFILE="seafile-data"
for d in conf1 conf2 conf3 conf4; do
for file in $CLEANFILE; do
rm -rf $d/$file
done
done
rm -rf worktree/*

View File

@ -0,0 +1,13 @@
[General]
USER_NAME=plt
ID=eed994152b231c673eeb5f586c06cd20cf9d10e8
NAME=plt
[Network]
DEFAULT_RELAY=8e4b13b49ca79f35732d9f44a0804940d985627c
PORT=10001
[Client]
PORT=10000

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEA1nXvjm57j9JfPf6zbjzMXhR0rz/xw4ZlX2XT5X1UZtAHe/rt
m7YNjlmxUT3XSU2xC+x+RCwCdCwZdm2xvwOeBHyS783TwdX9q3aFmVMppmYJ7b2C
rhzLIO7jQYy27TivP3TuUG/LILoYW16RIMvdnLWuZ+zF0PCUOZsbP041Tc6DNZ9D
nPbpRG4gzgcrlxLeKCF5qz0EYFQak7nuk0P+qz/DwTHCnNFcqLzcsmRiLrK9XqgS
2qi6U5nJApsILY4+Ep6EiiGGjdlSAVDpE5pHbte0hYWqO5ueLJ1RCHaXjYixLE+E
92SN4GFkTLPekHUcAqVIv23OZGhOPuKOwu1gJQIBIwKCAQAkw8Kqs9qq8N0gkhAh
hsPy3u9uf/2PO5xZfyujrxXIe27/T5ZydvsCdcagNnytewhoca8+4vkbOsKJVKIg
vsqhrvSeI0jfZoNB9w+WoIrMEX4Lf5K+wxuB/RED7DydsfIoIqyC0VYFnD6wk95d
ZMbnqh3l7hNIY8GjeazmR+vhc4uV3kpDiP1GNZPiX4J7y8mUFWwNTH4MlIevNMkz
ZY7SzOn105CLuT7iax+wcXLqDHIHYqqHVVnaHbc3hXGDvSVcYUHtUza7bsNvWJXn
uxTy2kAUlFRMLrbcYQXMH/U93V8Stu6fAID0OY+S8Zye272oJHDNzEH0B+rAGmhf
E55LAoGBAPNhYSYGBO/nBZTSHrw/ld5HBW8cQyaj4or00kmrg79tsb+osjBkRtDS
jX2hG4snCxu2+foXuUELlf52xFx9bjqknSS75KGvORjMZmON+u5cFAIAGEJT5pIh
twkokljkgApQcY9RsuzaS5gQKDtTBKCESHt3u/66mg9nkLlxJrPpAoGBAOGUrWxi
Ut6ZMAKou0uFWD1WnxRBZlPIZHgg3mSRRSjFOASpLXG0J0etaSY2sw4HtF3R7otY
KEJ6VnMdTEAvpf7TH187LNBD72/WEHBohrQ0ypINYXwrTzawBaYOfygVfVGZXYWw
2Vt1cLFjX5Gew/5+3ZCXNPmaSrrMDR8nKZDdAoGAFNx2CpLNOSJuMVPWv62twphf
jS5O5g4MGooDZWZ5AceS5IrNcdy1nN7Y7YLWeaRCx9x705uiKiWQg4aF3ArATitd
7TStMm4a1j1n3KXFDR3V1Ekmppl6KchgJVs/vnn8WKfPOCuS/lvamAFiiL35FRKn
IIabFdV662CmAUQ2hHMCgYEA1LDAxUbDKaZgd4klnvoRXmedTZy/kNLiYqKlzIj4
EH9vVNoGRqKLb3edp6/qptQQdbdOoKOTrGS36OEcAgEKOWf5AgSX95CCq0YswcGj
k/dCpvasXyGFM5AFU2zBCIlKRZ8668QAIwhUT3rztS9LFIY+nkVsdk+li40w8Xyy
KX8CgYADMlGFSWfVKZAu2WQMUs0SgJIEV9Z/G2kU15hvu2herSDil6oPy2cUb1L8
UF+y99upFndusiskaDOyxxtMESr7r3rlCeuOcvcHaLZyL/fam81YIG4Hc5HG7w+Y
Lw/Y4SYkYIUhQgjs/ZlS7jSuqmOtveRh0tB4IHi2v8CbTsH/hQ==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,10 @@
[General]
USER_NAME = server
ID = 8e4b13b49ca79f35732d9f44a0804940d985627c
NAME = server
[Network]
PORT = 10002
[Client]
PORT = 9999

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuZFwgxkKQGaqYyFMxIUz1JHnZPaOgEQ+fX/jRVYbGMiHkSbX
K9X3XUHUGEjUt8b3zW6UZJGjgyV5S08YuaN0eE5z6Q6bnuWEhkTmgZgXaybc9Hiu
y2WAHpKj+qbXcmewE0WEys/Ov9AIe0TRXmvL6r1793VcLSzgb/aIQA2WFg97DfEA
hGAHo5BesKRfEEvXL6ZB9cGxXP9qIy0ObTvLXlOgbYchfV4rrXJk0u9xWjRyXABv
2Myv3fgxmGmTR+TAw2G5GCKeh9IoIuWVMGPyjSlERGMqQYymNz3NgyWFayyZ5HQS
tihCnflOGEiMHRkOwIczB16YZhan2YqKpsjHGwIBIwKCAQEArvbXzBBLfoyvR4XM
Cb9rYgXozOh3usQAZ7MYHM2HQ0C6VahHN/WgFhl+1RF4Gv1tTKoW4nqwHJEL9oxn
xPkzTNxBZrYAcT7NaKdc/diLG+LQVDdFuHWkrxyL+vUUR0vR5kjcSjGlrYmhmMvb
WQaNEIbFVwhA92TTnMPfjNmcI2wRKI1K9NEKDAMIPSwW/sgkls2h4KW3Y7DooJ0k
l0apjN/rlaR4ohZp6oMVifW8GFY43Xau+4dIrYTnvvSyvGvtB+8cWuhqqvWHRZdM
rFjgOJoZH5l0zxt2dYW2WFiqgT7xXsvu6L+nylXktEMxC33rehYdPrd427J409A6
caO5cwKBgQDyrBQ8UXu7cDAktiKTwH7+pA0wNyTvKsGYw0RcFILccpxty2r5gYhI
eLFPVyjoYxwauW6vX3cSAYLKR+2PlYvkPpEvBQIJbaurx++ejez/KxYD65ZeFTfs
Kb9A08hgMxCvJmnRvojhez1OZmmmWYPT57XeZXnCiNoyJWKA0mMNvwKBgQDDwn02
o5n7ugetXIlV1PiStVogPPTBobh9jsXooQFh4fB+lsrO082hapMlbVVNG1gLzvTY
V0oDM/AzdnC6feZlAEdM+IcruinVnMnbnhiwPVDInCJIhvmJ/XScvkTsgHwRiAss
Tlf8wH/uGXiaeVV/KMlkKRK6h54znTPq37/VpQKBgQDkziG1NuJgRTS05j3bxB/3
Z3omJV1Wh2YTsMtswuHIiVGpWWTcnrOyC2VZb2+2iVUDQR83oycfmwZJsYg27BYu
+SnNPzxvSiWEtTJiS00rGf7QfwoeMUNbAspEb+jPux5b/6WZ34hfkXRRO/02cagu
Mj3DDzhJtDtxG+8pAOEM9QKBgQC+KqWFiPv72UlJUpQKPJmzFpIQsD44cTbgXs7h
+32viwbhX0irqS4nxp2SEnAfBJ6sYqS05xSyp3uftOKJRxpTfJ0I8W1drYe5kP6a
1Bf7qUcpRzc/JAhaKWn3Wb9MJQrPM7MVGOfCVJmINgAhCCcrEa2xwX/oZnxsp1cB
a6RpIwKBgQDW15IebNwVOExTqtfh6UvIjMSrk9OoHDyjoPLI3eyPt3ujKdXFJ8qF
CWg9ianQyE5Y8vfDI+x1YRCOwq2WapeXzkSO8CzVFHgz5kFqJQolr4+o6wr5mLLC
+6iW9u81/X3bMAWshtNfsWbRSFLT1WNVTKRg+xO7YG/3wcyeIeqigA==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,10 @@
[General]
USER_NAME = freeplant
ID = e19d1070b6d9ae7bf453d124ff4e775076553906
NAME = freeplant
[Network]
PORT = 10003
[Client]
PORT = 9998

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAntQ0Wds67rFiy5/Wq30EzOpRYFPGC7xOCS+tBTwC+YUl9fSE
5kzicoC699EDf5N6Ky4u4j2KhXIkwCwQO6EOxTQjG7Gyxj3YS63lU/zgGaGI9L3G
xkkCPvqpYCuxHYELClud/3Zb/xA0aZr4EZiLHs21nUSuchVftN8URG6OJxf18Tji
1nysBmaZ9jLbuHpaELV1Rc+lGsECslfpjITm/bRcsW1VgrjJXv1dtekIRzpoKMVB
u89nO3YhRbC2qpQR27BsfeldIQYxWiD4jNC9LfvCsxgfEYpo/W5Y/rImCaNatqff
wQ0rUCWjwvAypU1iLhPlL6fG75iU5dDgCKuYKwIBIwKCAQBs6Uh4IUz7clJfvg+L
iO1ZUDfNBj6o9ibhukrBwsA2Eiiop6uWmx7vbjcQVM8y6MjNJvuT0m2dVZWLFuaP
Sd4+FR9jcok3et11uRJIOF8nhLWvIwv1rmfwq+Hbi6ymhF9Xj0fFHfXwuqeY30sE
wF9llF9HRQnn1CRexNqylPPDCAkiEclXyBOhpk7TIkVhCfKagDtivjORRF2OBKUb
A8x7mei/AjbqovYbWNUmsX++PpUiau7y4WumiTFOPVhtynQv7DVRAC2lw+NUNFU0
4/JhYyTvx/KbVzpwhMfTHE1bEErqzQrZsXH9rkrq5/tm3QeCfYZVsNyjaAOR3kxK
HAPLAoGBAMpVOxCF9q4kQWLzRMW+NwDGN0HNKZqfrbtJebQeqeRQPbfDAPJIITfh
TfMP5XtGL/rCA/sTHOlMi9Dr0a6229q8BNjtDBEi+HeK7OS8O1mCDt8mQ5npTfO9
FY3ZShJvKIhHgWKtc/tkXwGG/DesLSJVWxpQsyaQhjEVArxAewJNAoGBAMj0+Tdb
LOFBEq65sXDMv4IpLdzd+1Scj5RPF29RICMj4NwtM9MkaeqVzd54QLK2RH6Uj/+G
fxZGt5MdGub+Rk2vHcXQEl7BpWOfEmzjc6IzPE2vJkNbB+qxlye8NCLLAw/Yu3fr
dmmvKHT6twGtyJTlFxGh/V6bcAlGv0ncHRBXAoGBAL7FY5M8et60eCoYkUy6qON5
D4cvJzoERLfmMOun4gMnFZ6h6vMQztzqX3Ak7k+ok6NJNvQKtNv3twbPtxJx5Tvz
GoNjKKJxbe0N9Uyxeclr/2SvDInNWCBTMZRfKJUJuIB97wVEdKsyv/ogL6I0n5Vm
a9b7oZljsbk/sh81e0tPAoGAROZG0SaTC2bEk61haIC2sEiiApVAOkROe/3cJipx
ayI+d1/0gufpzMWlq1xt9CE8DiROoMBmFkQhrsgmexVZ7r+yb7UNnNStu8GCqP2G
u0TEN+RHohCVAAJftdoggPUlnwh6ygeWUB7MCtmd1LCcitIzy4f/GR9ZnMfMjloY
l+MCgYAmpvxULeaYSrA00iZM2mfyXk6Jlm01IVXd7LR0qswghjzGGyJxgyU90FMg
KTW+wzDWMWD1AegyeLNjRwWDLtvF+HJlvT2O6P9hQLlZQnLuM+yOQtAjb0vzbued
LoXUAsUJP8LIAbY8WgO4F7w5j71HBekJZRndlU+4/m/uajiLsg==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,10 @@
[General]
USER_NAME = server2
ID = 9b7adea2e0a954196734af25837751bb833336e6
NAME = server2
[Network]
PORT = 10004
[Client]
PORT = 9997

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA0uSgFWuzO3fWwf4pAes+Nfd9ebBmPjNrKks4oVEZ3pXMWu+u
iCE9az6gBmhxYDPXLXkldK9gh7qG7/Ma3wL+3pQ4jvdvI5PXY23n36K+Uww2iTEb
HLz8AM/BfN2hIxZxYy4DDWMeWj3njquSyw0Lk843Rkay1w9ZRj968cQTzwSKelVG
gkH+SR03VSFn7lcnCZ3fsncK19KDXagwjIWizYszYfmI6LbImcko24fQwp7MyrhH
tMJ6R/GFsRs9F7QTe+IUJCnOTCJfFIl/TaWnHLA9g6DN/M+v4PBAbaR5xtCkWVuF
/azRdeKZDvS0MKSMQxtT4RThp2FBt8xdXyPZrwIBIwKCAQByfBxjZln7thV/QNRu
wYgr7MB1QoClbF6/MCYObdrX6utHTum3nQQVpadT744BBjL7baaebdVQ/tuJkpmP
AaBM8WCIH+vgHQ6Gbtz1vr8XI+MXRosPmcqhWtbAIIqspciGTC2K7KoTuzSPRzJu
OkgdCYu4b4Wn8mOpyrB0naRabzF3F+tC/T1QlZt8mHbpmKAMTLhjh1B39v2aemaa
v1mv3dRFeJb6YAPr91YXzVN+ZAEtKbda7guCPdswzFh+y2UsHTASb9NGOYjl+CKm
Jh1PJFqQOMjlPSFFpbwut99l8BvAAiayq2wf6o1cdvsLDxxrbJ35Tmv0+DpFS2Uk
hjM7AoGBAPFRSOw7w9sLuFaHtKA5/otqEbbBgsSybj0+QGhZ8lMn9bMTMTkha/SO
nWK0dYGNl3xefGbDcmaS+EVq5jgSkwwnbZODC7MP2YpeXs6QHoJO1bv1KR9qBFPo
iLSCK9TQy8BgXCGUZU4q1Hz3d3sUgsJoD6YF8WmEiQ8HoiF76OmJAoGBAN+5dIB6
P9q2bDukidG1MfuI4+LAWVOZPvmx/5HtF+BUJUofps3fILHfO7vFVLVnJqySTVlJ
z3fFDgrRXkcOFiSJ7eS1DL53jgDzl35jCwnCzW7wldC2nh6uAFmwW9OLFLDaqpZj
hP98DTKjDdZzjLBzeX1gu23mUk4Vkd/jUTN3AoGAfBss0UNOyGxtblRrhZorIyCb
ZU2TtZ2Xx7mdd363I3Oi7mGkSURjZ9RQ8O8X3Dowplx6ff4dk9aOT5YP/5SGI4H9
1tWuPtTziP1VVEoeUaTi8vMcdo5LXlpU7x5fr0bPL7yHJzZf/FCKiWlTY96ppc8d
/ZyoCmFrDws9cE5ahrsCgYEAzIxNQkPinBSANofOdpcJIH0oH8XOA0pINL//m1xt
meaIfkjTBV5JxzJ/wZ55VWWfs7j2QwGnvfX+Nca8mLxsBCZV2GsEVl6tt7okG8hE
mzXDI5oikvAbicOojIP8LyAS6taNVkxcV0zY3dboFIbnB7oIrSU9pk7sKiJZfD2M
EcsCgYEA4U2nISZLqt4ti+ruy4URVRJXK8WLSAm42c4MtpR6Z/2dlNvPGxPSdWra
hUYl6x8AOITNkthkLp0+oDewO8iXI5U3RnrrKm7Uuu6df0F/sr0XW/Nh+DrG2o70
xgVv7UlOE2h/VQklxYj/smSIT6TQh/7cemMFexICIOkOAI4e57E=
-----END RSA PRIVATE KEY-----

27
tests/basic/lan.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
conf2=${testdir}/conf2
conf3=${testdir}/conf3
conf4=${testdir}/conf4
debug=Message,Requirement,Other,Peer,Group,Kvitem
if [ ! -d ${conf1}/logs ]; then
mkdir ${conf1}/logs
fi
if [ ! -d ${conf3}/logs ]; then
mkdir ${conf3}/logs
fi
gnome-terminal -e "${ccnet} -c ${conf1} -D ${debug} -f -"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -d ${conf1}/seafile-data -w worktree/wt1 -D all -l -"
gnome-terminal -e "${ccnet} -c ${conf3} -D ${debug} -f -"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf3} -d ${conf3}/seafile-data -w worktree/wt3 -D all -l -"

53
tests/basic/seafile.sh Executable file
View File

@ -0,0 +1,53 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
conf2=${testdir}/conf2
conf3=${testdir}/conf3
conf4=${testdir}/conf4
debug=Message,Other,Peer
if [ ! -d ${conf1}/logs ]; then
mkdir ${conf1}/logs
fi
if [ ! -d ${conf3}/logs ]; then
mkdir ${conf3}/logs
fi
if [ ! -d ${conf4}/logs ]; then
mkdir ${conf4}/logs
fi
while [ $# -ge 1 ]; do
case $1 in
"1" )
gnome-terminal -e "${ccnet} -c ${conf1} -D ${debug} -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -d ${conf1}/seafile-data -w worktree/wt1 -D all -l -"
;;
"2" )
# Use sqlite as database in testing
../../tools/seaf-server-init -d ${conf2}/seafile-data > /dev/null
gnome-terminal -e "${ccnet_server} -c ${conf2} -D ${debug} -f -"
sleep 3
gnome-terminal -e "${seaf_server} -c ${conf2} -d ${conf2}/seafile-data -f -l - "
sleep 3
gnome-terminal -e "${seaf_monitor} -c ${conf2} -d ${conf2}/seafile-data -f -l -"
;;
"3" )
gnome-terminal -e "${ccnet} -c ${conf3} -D ${debug} -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf3} -d ${conf3}/seafile-data -w worktree/wt3 -D all -l -"
;;
"4" )
gnome-terminal -e "${ccnet} -c ${conf4} -D ${debug} -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf4} -d ${conf4}/seafile-data -w worktree/wt4 -D all -l -"
;;
esac
shift
done

45
tests/basic/setup.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
conf2=${testdir}/conf2
conf3=${testdir}/conf3
conf4=${testdir}/conf4
peer1=c882e263e9d02c63ca6b61c68508761cbc74c358
peer2=376cf9b6ef33a6839cf1fc096131893b5ecc673f
peer3=1e5b5e0f49010b94aa6c2995a6e7b7cba462d388
peer4=93ae3e01eea6667cbdd03c4afde413ccd9f1eb43
${ccnet_server} -c ${conf2} &
${ccnet} -c ${conf1} --no-multicast -D Peer,Requirement,Group,Syncher,Message,Connection,Other -f - &
${ccnet} -c ${conf3} --no-multicast &
${ccnet} -c ${conf4} --no-multicast &
sleep 5
# ${ccnet_tool} -c ${conf1} set-relay --default --addr 127.0.0.1:10002
# ${ccnet_tool} -c ${conf3} set-relay --default --addr 127.0.0.1:10002
# ${ccnet_tool} -c ${conf4} set-relay --default --addr 127.0.0.1:10002
# sleep 20
echo "+++ Added relay"
# ${ccnet_servtool} -c ${conf2} add-client ${peer1}
# ${ccnet_servtool} -c ${conf2} add-client ${peer3}
# ${ccnet_servtool} -c ${conf2} add-client ${peer4}
# sleep 30
echo "+++ Added client"
#sleep 60
#echo "+++ Add chunk servers"
#./seafserv-tool -c conf2 add-server server
#./seafserv-tool -c conf2 add-server server2
echo "+++ clean up"
pkill -2 ccnet

14
tests/basic/teardown.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
CLEANFILE="requirement-db object-db peer-db group-db \
user-db GroupMgr PeerMgr ccnet.log \
seafile-data misc logs"
for d in conf1 conf2 conf3 conf4; do
for file in $CLEANFILE; do
rm -rf $d/$file
done
done
rm -rf conf2/ccnet.db
rm -rf worktree/*

View File

@ -0,0 +1,48 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
./clean.sh
./teardown.sh
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/`
mkdir -p ${worktree}/wt1/${repo_id}/test1/test2
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}
cp ${top_srcdir}/autogen.sh ${worktree}/wt1/${repo_id}/test1
cp ${top_srcdir}/configure.ac ${worktree}/wt1/${repo_id}/test1/test2
sleep 1
# add a file
${seafile_app} -c ${conf1} add ${repo_id} README > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# include space
${seafile_app} -c ${conf1} branch add ${repo_id} "test test"
# include tab
${seafile_app} -c ${conf1} branch add ${repo_id} "test test"
# include enter
${seafile_app} -c ${conf1} branch add ${repo_id} "test test"
# show branch
${seafile_app} -c ${conf1} branch show ${repo_id}

44
tests/basic/test-branch-show.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
./clean.sh
./teardown.sh
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/`
mkdir -p ${worktree}/wt1/${repo_id}/test1/test2
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}
cp ${top_srcdir}/autogen.sh ${worktree}/wt1/${repo_id}/test1
cp ${top_srcdir}/configure.ac ${worktree}/wt1/${repo_id}/test1/test2
sleep 1
# add a file
${seafile_app} -c ${conf1} add ${repo_id} README > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# add branch
${seafile_app} -c ${conf1} branch add ${repo_id} test
# show branch
${seafile_app} -c ${conf1} branch show ${repo_id}

63
tests/basic/test-diff-rename.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
PWD=`pwd`
./clean.sh
./teardown.sh
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/ | grep -v "checkout-files"`
# create some files
mkdir -p ${worktree}/wt1/${repo_id}/test/test
echo "rename" >>${worktree}/wt1/${repo_id}/test/test/rename
#cp -rf /tmp/ccnet ${worktree}/wt1/${repo_id}/
sleep 1
# add some files
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# add branch
${seafile_app} -c ${conf1} branch add ${repo_id} test
sleep 1
# checkout to test branch
${seafile_app} -c ${conf1} checkout ${repo_id} test
sleep 1
# modify file
#mv -f ${worktree}/wt1/${repo_id}/ccnet ${worktree}/wt1/${repo_id}/xx
mv -f ${worktree}/wt1/${repo_id}/test ${worktree}/wt1/${repo_id}/test1
sleep 1
# add file
${seafile_app} -c ${conf1} add ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit2
sleep 1
# diff
${seafile_app} -c ${conf1} diff ${repo_id} test local

65
tests/basic/test-diff.sh Executable file
View File

@ -0,0 +1,65 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
repo_name="test-repo"
./clean.sh
./teardown.sh
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/systems`
# create some files
echo "modify" >> ${worktree}/wt1/works/${repo_name}/modify
echo "remove" >> ${worktree}/wt1/works/${repo_name}/remove
echo "unmerge" >> ${worktree}/wt1/works/${repo_name}/unmerge
sleep 1
# add some files
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# add branch
${seafile_app} -c ${conf1} branch add ${repo_id} test
sleep 1
# checkout to test branch
${seafile_app} -c ${conf1} checkout ${repo_id} test
sleep 1
# modify file
echo "add" >> ${worktree}/wt1/works/${repo_name}/add
echo "modify" >> ${worktree}/wt1/works/${repo_name}/modify
rm ${worktree}/wt1/works/${repo_name}/remove
echo "merge conflict" > ${worktree}/wt1/works/${repo_name}/unmerge
sleep 1
# add file
${seafile_app} -c ${conf1} add ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit2
sleep 1
# diff
${seafile_app} -c ${conf1} diff ${repo_id} test local

67
tests/basic/test-diff2.sh Executable file
View File

@ -0,0 +1,67 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
./clean.sh
./teardown.sh
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/`
# mkdir
mkdir ${worktree}/wt1/${repo_id}/diff
# create some files
echo "modify" >> ${worktree}/wt1/${repo_id}/diff/modify
echo "remove" >> ${worktree}/wt1/${repo_id}/diff/remove
echo "unmerge" >> ${worktree}/wt1/${repo_id}/diff/unmerge
sleep 1
# add some files
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# add branch
${seafile_app} -c ${conf1} branch add ${repo_id} test
sleep 1
# checkout to test branch
${seafile_app} -c ${conf1} checkout ${repo_id} test
sleep 1
# modify file
echo "add" >> ${worktree}/wt1/${repo_id}/diff/add
echo "modify" >> ${worktree}/wt1/${repo_id}/diff/modify
rm ${worktree}/wt1/${repo_id}/diff/remove
echo "merge conflict" > ${worktree}/wt1/${repo_id}/diff/unmerge
sleep 1
# add file
${seafile_app} -c ${conf1} add ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit2
sleep 1
# diff
${seafile_app} -c ${conf1} diff ${repo_id} test local

62
tests/basic/test-diff3.sh Executable file
View File

@ -0,0 +1,62 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
PWD=`pwd`
./clean.sh
./teardown.sh
rm -rf ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/`
# create some files
cp -rf ~/projects/gonggeng/ccnet ${worktree}/wt1/${repo_id}/
sleep 1
# add some files
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# add branch
${seafile_app} -c ${conf1} branch add ${repo_id} test
sleep 1
# checkout to test branch
${seafile_app} -c ${conf1} checkout ${repo_id} test
sleep 1
# modify file
cd ${worktree}/wt1/${repo_id}/ccnet
git checkout master
cd $PWD
sleep 1
# add file
${seafile_app} -c ${conf1} add ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit2
sleep 1
# diff
${seafile_app} -c ${conf1} diff ${repo_id} test local

View File

@ -0,0 +1,93 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
repo_name="test-repo"
./clean.sh
./teardown.sh
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
rm ${conf1}/seafile/repo.db
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnet1`
repoid1=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnet2`
repoid2=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnet3`
repoid3=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnet4`
repoid4=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnet5`
repoid5=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnet6`
repoid6=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
ls /tmp
sleep 2
${seafile_app} -c ${conf1} repo-rm ${repoid1} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid2} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid3} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid4} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid5} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid6} > /dev/null
ls /tmp
# create a repo
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test`
repoid1=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test`
repoid2=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test`
repoid3=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test`
repoid4=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test`
repoid5=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test`
repoid6=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
ls /tmp/worktree/wt1/systems
sleep 2
${seafile_app} -c ${conf1} repo-rm ${repoid1} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid2} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid3} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid4} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid5} > /dev/null
sleep 1
${seafile_app} -c ${conf1} repo-rm ${repoid6} > /dev/null
ls /tmp/worktree/wt1/systems

View File

@ -0,0 +1,20 @@
Objectives
===
Test the repo-encryption functionality. One should be able to upload
an encrypt repo, and fetch it elsewhere, and can checkout this repo
correctly as long as the right passwd is set.
Test
===
The test flow is 90% the same as the basic test. Only two steps are
different:
1. Use `seafile create --encrypt` to create an encrypted repo, rather
than using a plain `seafile create`. You need to input the passwd
manually.
2. After fetching is finished, use `seafile set-passwd` to set the
passwd for the encrypted repo, and then checkout it.

72
tests/basic/test-encrypt.sh Executable file
View File

@ -0,0 +1,72 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
conf3=${testdir}/conf3
worktree=${testdir}/worktree
seafile_app=${seafile_dir}/app/seafile
echo "+++ start seafile"
./seafile.sh 1 2 3 4
repo_id=`ls ${worktree}/wt1/`
if [ x${repo_id} != x ]; then
echo "Worktree is not empty, perform ./clean.sh first"
exit 1
fi
function create_repo
{
# create a repo
${seafile_app} -c ${conf1} create --encrypt --passwd=1234 test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/ | tail -n 1`
if [ -z ${repo_id} ]; then
echo "create repo failed"
exit 1
fi
mkdir -p ${worktree}/wt1/${repo_id}/test1/test2
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}
cp ${top_srcdir}/autogen.sh ${worktree}/wt1/${repo_id}/test1
cp ${top_srcdir}/configure.ac ${worktree}/wt1/${repo_id}/test1/test2
sleep 1
# add files
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
}
echo "+++ create repo"
create_repo
repo_id=`ls ${worktree}/wt1/`
echo "+++ upload ${repo_id}"
${seafile_app} -c ${conf1} upload ${repo_id} local master
sleep 10
echo "+++ fetch ${repo_id}"
${seafile_app} -c ${conf3} fetch ${repo_id} master master
sleep 10
echo "+++ checkout"
${seafile_app} -c ${conf3} branch add ${repo_id} local master
${seafile_app} -c ${conf3} set-passwd ${repo_id} 1234
${seafile_app} -c ${conf3} checkout ${repo_id} local
echo "+++ check diff"
if diff ${worktree}/wt3/${repo_id}/test1/test2/configure.ac \
${worktree}/wt1/${repo_id}/test1/test2/configure.ac > /dev/null ; then
echo "+++ Success"
else
echo "+++ failed"
fi
echo "+++ cleanup"
pkill ccnet

View File

@ -0,0 +1,82 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
./clean.sh
./teardown.sh
rm -rf ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/`
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}
sleep 1
${seafile_app} -c ${conf1} status ${repo_id}
# add a file
${seafile_app} -c ${conf1} add ${repo_id} README > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# add two branches
${seafile_app} -c ${conf1} branch add ${repo_id} test1
sleep 1
${seafile_app} -c ${conf1} branch add ${repo_id} test2
sleep 1
# checkout to test1
${seafile_app} -c ${conf1} checkout ${repo_id} test1
sleep 1
# modify file content
echo "test1" >> ${worktree}/wt1/${repo_id}/README
sleep 1
# add README
${seafile_app} -c ${conf1} add ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} test1-commit1
sleep 1
# checkout to test2
${seafile_app} -c ${conf1} checkout ${repo_id} test2
sleep 1
# modify file content
echo "test2" >> ${worktree}/wt1/${repo_id}/README
sleep 1
# add README
${seafile_app} -c ${conf1} add ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} test2-commit2
sleep 1
# status
${seafile_app} -c ${conf1} status ${repo_id}
#
# all above should be OK
#
# merge test1 into test2
${seafile_app} -c ${conf1} merge ${repo_id} test1
# status
${seafile_app} -c ${conf1} status ${repo_id}

View File

@ -0,0 +1,45 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
repo_name="test-repo"
./clean.sh
./teardown.sh
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
rm ${conf1}/seafile/repo.db
rm -rf /tmp/ccnet1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
tmp=`${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnet1`
repoid1=`echo $tmp | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
cp README /tmp/ccnet1/
${seafile_app} -c ${conf1} add ${repoid1} >/dev/null
sleep 1
${seafile_app} -c ${conf1} commit ${repoid1} "init" >/dev/null
sleep 1
echo "----"
ls /tmp/ccnet1/
sleep 1
rm -rf /tmp/ccnet1/
sleep 1
${seafile_app} -c ${conf1} checkout ${repoid1} local /tmp/haha/ >/dev/null
sleep 1
echo "----"
ls /tmp/haha

View File

@ -0,0 +1,49 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
repo_name="test-repo"
./clean.sh
./teardown.sh
# create dir
if [ $1 = "create" ]; then
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
rm ${conf1}/seafile/repo.db
fi
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
# for debug
if [ $1 = "debug" ]; then
read tmp
else
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
fi
# create a repo
if [ $1 = "create" ]
then
#${seafile_app} -c ${conf1} create ${repo_name} test > /dev/null
#mkdir /tmp/ccnet-test
${seafile_app} -c ${conf1} create ${repo_name} test /tmp/ccnett > /dev/null
fi
repo_id=`ls ${worktree}/wt1/systems`
ls -R ${worktree}
# add file
cp README ${worktree}/wt1/works/${repo_name}/
${seafile_app} -c ${conf1} add ${repo_id} >/dev/null
# commit file
${seafile_app} -c ${conf1} commit ${repo_id} "init" >/dev/null

View File

@ -0,0 +1,63 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=${testdir}/worktree
seafile_app=${seafile_dir}/app/seafile
echo "+++ start seafile"
./seafile.sh 1 2
sleep 1
# create a new repo
res=`${seafile_app} -c ${conf1} create test-repo test ${worktree}/wt1`
# get repo id
repo_id=`echo ${res} | awk '{print $6}' | awk -F. '{print $1}'`
sleep 1
# add some files
mkdir ${worktree}/wt1/1/
cp ${top_srcdir}/configure ${worktree}/wt1/
cp ${top_srcdir}/configure ${worktree}/wt1/1/
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1
# start to test put-file API
echo "+++ start to stest put-file API ..."
# success
echo "+++ pass"
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README ./
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README /
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README 1
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README 1/
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README /1
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README /1/
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README ./1/
sleep 1
# failure
echo "++++ failures
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README ./1/2
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README ./1//
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README ./1/2//
sleep 1
${seafile_app} -c ${conf1} put-file ${repo_id} ${top_srcdir}/README .//1/
sleep 1
echo "+++ cleanup"
pkill ccnet

56
tests/basic/test-rm.sh Executable file
View File

@ -0,0 +1,56 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
./clean.sh
./teardown.sh
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/`
mkdir -p ${worktree}/wt1/${repo_id}/test1/test2
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}
cp ${top_srcdir}/autogen.sh ${worktree}/wt1/${repo_id}/test1
cp ${top_srcdir}/configure.ac ${worktree}/wt1/${repo_id}/test1/test2
sleep 1
# add a file
${seafile_app} -c ${conf1} add ${repo_id} README > /dev/null
${seafile_app} -c ${conf1} add ${repo_id} test1/autogen.sh > /dev/null
${seafile_app} -c ${conf1} add ${repo_id} test1/test2/configure.ac > /dev/null
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
rm ${worktree}/wt1/${repo_id}/README
${seafile_app} -c ${conf1} rm ${repo_id} README
${seafile_app} -c ${conf1} rm ${repo_id} test1/autogen.sh
${seafile_app} -c ${conf1} status ${repo_id}
${seafile_app} -c ${conf1} commit ${repo_id} commit2
cp ${top_srcdir}/configure.ac ${worktree}/wt1/${repo_id}/test1/test2
${seafile_app} -c ${conf1} status ${repo_id}
echo "hello" >> ${worktree}/wt1/${repo_id}/test1/test2/configure.ac
${seafile_app} -c ${conf1} rm ${repo_id} test1/test2/configure.ac
rm -rf ${worktree}

59
tests/basic/test-share.sh Executable file
View File

@ -0,0 +1,59 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
function create_repo
{
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/`
mkdir -p ${worktree}/wt1/${repo_id}/test1/test2
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}
cp ${top_srcdir}/autogen.sh ${worktree}/wt1/${repo_id}/test1
cp ${top_srcdir}/configure.ac ${worktree}/wt1/${repo_id}/test1/test2
sleep 1
# add a file
${seafile_app} -c ${conf1} add ${repo_id} README > /dev/null
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
}
rm -r ${worktree}/wt1
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# find the group id
group_id=`ls ${conf1}/group-db/ | tail -n 1`
if [ -z $group_id ]; then
echo "no group exists. You may forget to run ./setup.sh"
exit 1
fi
# find a repo
repo_id=`ls ${worktree}/wt1/`
if [ -z $repo_id ]; then
create_repo
repo_id=`ls ${worktree}/wt1/`
fi
echo "repo id is $repo_id"
echo "+++ share item"
item_id=`${seafile_app} -c ${conf1} share ${repo_id} ${group_id}`
echo "+++ unshare item"
${seafile_app} -c ${conf1} unshare ${item_id}

View File

@ -0,0 +1,56 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
./clean.sh
./teardown.sh
rm -rf ${worktree}
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
#read tmp
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/ | grep -v "checkout-files"`
mkdir -p ${worktree}/wt1/${repo_id}/test1/test2
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}/test1/test2
sleep 1
echo "----------------------"
${seafile_app} -c ${conf1} status ${repo_id}
sleep 1
# add a file
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
echo "----------------------"
${seafile_app} -c ${conf1} status ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
# rename file
mv ${worktree}/wt1/${repo_id}/test1 ${worktree}/wt1/${repo_id}/"1ts et"
sleep 1
# add a file
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
echo "----------------------"
${seafile_app} -c ${conf1} status ${repo_id}

View File

@ -0,0 +1,47 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
./clean.sh
./teardown.sh
rm -rf ${worktree}
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create test-repo test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/ | grep -v "checkout-files"`
cp ${top_srcdir}/README ${worktree}/wt1/${repo_id}/
touch -t 191305091843 ${worktree}/wt1/${repo_id}/README
sleep 1
echo "----------------------"
${seafile_app} -c ${conf1} status ${repo_id}
sleep 1
# add a file
${seafile_app} -c ${conf1} add ${repo_id} > /dev/null
sleep 1
echo "----------------------"
${seafile_app} -c ${conf1} status ${repo_id}
sleep 1
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
sleep 1
echo "----------------------"
${seafile_app} -c ${conf1} status ${repo_id}

63
tests/basic/test-status.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
. ../common-conf.sh
testdir=${seafile_dir}/tests/basic
conf1=${testdir}/conf1
worktree=/tmp/worktree
seafile_app=${seafile_dir}/app/seafile
repo_name="test-repo"
./clean.sh
./teardown.sh
mkdir -p ${worktree}/wt1
gnome-terminal -e "${ccnet} -c ${conf1} -D all -f - --no-multicast"
sleep 3
gnome-terminal -e "${seaf_daemon} -c ${conf1} -w ${worktree}/wt1 -l -"
sleep 3
# create a repo
${seafile_app} -c ${conf1} create ${repo_name} test > /dev/null
sleep 3
repo_id=`ls ${worktree}/wt1/systems`
mkdir -p ${worktree}/wt1/works/${repo_name}/test1/test2
cp ${top_srcdir}/README ${worktree}/wt1/works/${repo_name}
cp ${top_srcdir}/autogen.sh ${worktree}/wt1/works/${repo_name}/test1
cp ${top_srcdir}/configure.ac ${worktree}/wt1/works/${repo_name}/test1/test2
sleep 1
${seafile_app} -c ${conf1} status ${repo_id}
# add a file
${seafile_app} -c ${conf1} add ${repo_id} README > /dev/null
${seafile_app} -c ${conf1} status ${repo_id}
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit1 > /dev/null
# add a file
${seafile_app} -c ${conf1} add ${repo_id} test1/autogen.sh > /dev/null
${seafile_app} -c ${conf1} add ${repo_id} test1/test2/configure.ac > /dev/null
${seafile_app} -c ${conf1} status ${repo_id}
# commit
${seafile_app} -c ${conf1} commit ${repo_id} commit2 > /dev/null
rm ${worktree}/wt1/works/${repo_name}/README
echo "hello" >> ${worktree}/wt1/works/${repo_name}/test1/test2/configure.ac
${seafile_app} -c ${conf1} status ${repo_id}
echo "hello" >> ${worktree}/wt1/works/${repo_name}/test1/autogen.sh
sleep 1
${seafile_app} -c ${conf1} add ${repo_id} test1/autogen.sh
${seafile_app} -c ${conf1} status ${repo_id}
rm -rf ${worktree}

13
tests/common-conf.sh.in Normal file
View File

@ -0,0 +1,13 @@
top_srcdir=@abs_top_srcdir@
ccnet=${top_srcdir}/net/daemon/ccnet
ccnet_server=${top_srcdir}/net/server/ccnet-server
ccnet_applet=${top_srcdir}/gtk/ccnet-applet
ccnet_servtool=${top_srcdir}/cli/ccnet-servtool
ccnet_tool=${top_srcdir}/cli/ccnet-tool
seafile_dir=${top_srcdir}/seafile
seaf_daemon=${seafile_dir}/daemon/seaf-daemon
seaf_server=${seafile_dir}/server/seaf-server
seaf_monitor=${seafile_dir}/monitor/seaf-mon

78
tests/common.py Normal file
View File

@ -0,0 +1,78 @@
import sys
#sys.path = ['../../python', '../../python/ccnet/.libs', '../../python/pyccnetevent/.libs', '../python', '../python/seafile/.libs', '../../lib/searpc', '../../lib/searpc/pysearpc/.libs'] + sys.path
from datetime import datetime
import os
import sqlite3
import ccnet
class CcnetDaemon(object):
def __init__(self, confdir):
self.confdir = confdir
def start(self, *args):
self.child_pid = os.fork()
if not self.child_pid:
# child
#os.execl("../net/ccnet", "ccnet", "-c", self.confdir,
# "-D", "All", "-f", "-")
os.execl("../../net/ccnet", "ccnet", "-c", self.confdir,
"-D", "All", *args)
def stop(self):
os.kill(self.child_pid, 2)
class SeafileDaemon(object):
def __init__(self, confdir):
self.confdir = confdir
def start(self, *args):
self.child_pid = os.fork()
if not self.child_pid:
# child
#os.execl("../net/ccnet", "ccnet", "-c", self.confdir,
# "-D", "All", "-f", "-")
os.execl("../daemon/seaf-daemon", "seaf-daemon", "-c",
self.confdir, *args)
def stop(self):
os.kill(self.child_pid, 2)
def get_client_sync(confdir):
client = ccnet.Client()
client.load_confdir(confdir)
client.connect_daemon(ccnet.CLIENT_SYNC)
return client
def get_client_async(confdir):
client = ccnet.Client()
client.load_confdir(confdir)
sockfd = client.connect_daemon(ccnet.CLIENT_ASYNC)
if sockfd < 0:
print "Can't connect to daemon"
exit()
client.run_synchronizer()
client.sockfd = sockfd
return client
def print_cmsg(msg):
print >>sys.stderr, "[**Control %s] %s" % (
datetime.now().strftime("%H:%M:%S.%f"), msg)
def db_item_exists(dbfile, sql):
"""Check whether `sql` returns any records in sqlite db `dbfile`."""
conn = sqlite3.connect(dbfile)
c = conn.cursor()
c.execute(sql)
if c.fetchone():
ret = True
else:
ret = False
conn.close()
return ret

125
tests/test-cdc.c Normal file
View File

@ -0,0 +1,125 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
#include <cdc.h>
char *dest_dir = NULL;
static void rawdata_to_hex (const unsigned char *rawdata,
char *hex_str, int n_bytes)
{
static const char hex[] = "0123456789abcdef";
int i;
for (i = 0; i < n_bytes; i++) {
unsigned int val = *rawdata++;
*hex_str++ = hex[val >> 4];
*hex_str++ = hex[val & 0xf];
}
*hex_str = '\0';
}
int test_chunks (CDCFileDescriptor *file_descriptor)
{
struct stat sb;
char chksum_str[CHECKSUM_LENGTH *2 + 1];
char filename[NAME_MAX_SZ];
uint8_t *ptr = file_descriptor->blk_sha1s;
int i = 0;
int max_sz = -1, min_sz = INT_MAX, total_sz = 0;
printf ("%d chunks.\n", file_descriptor->block_nr);
while (i < file_descriptor->block_nr) {
rawdata_to_hex (ptr, chksum_str, CHECKSUM_LENGTH);
snprintf (filename, NAME_MAX_SZ, "%s/%s", dest_dir, chksum_str);
if (g_stat (filename, &sb) < 0) {
perror ("stat");
return -1;
}
if (sb.st_size < min_sz)
min_sz = sb.st_size;
if (sb.st_size > max_sz)
max_sz = sb.st_size;
total_sz += sb.st_size;
if (sb.st_size > file_descriptor->block_max_sz) {
fprintf (stderr, "chunk size too large: %s.\n", chksum_str);
return -1;
}
if (sb.st_size < file_descriptor->block_min_sz &&
i != file_descriptor->block_nr - 1) {
fprintf (stderr, "chunk size too small: %s.\n", chksum_str);
return -1;
}
ptr += CHECKSUM_LENGTH;
++i;
}
printf ("max size: %d\n", max_sz);
printf ("min size: %d\n", min_sz);
printf ("avg size: %d\n", total_sz/file_descriptor->block_nr);
return 0;
}
int test_write_chunk (CDCDescriptor *chunk_descr,
struct SeafileCrypt *crypt,
uint8_t *checksum,
gboolean write_data)
{
char filename[NAME_MAX_SZ];
char chksum_str[CHECKSUM_LENGTH *2 + 1];
int fd_chunk, ret;
rawdata_to_hex (chunk_descr->checksum, chksum_str, CHECKSUM_LENGTH);
snprintf (filename, NAME_MAX_SZ, "%s/%s", dest_dir, chksum_str);
fd_chunk = g_open (filename, O_WRONLY | O_CREAT | O_BINARY, 0644);
if (fd_chunk < 0)
return -1;
ret = write (fd_chunk, chunk_descr->block_buf, chunk_descr->len);
return ret;
}
int main (int argc, char *argv[])
{
char *src_filename = NULL;
int ret = 0, fd_src;
CDCFileDescriptor file_descr;
if (argc < 3) {
fprintf(stderr, "%s SOURCE DEST \n", argv[0]);
exit(0);
} else {
src_filename = argv[1];
dest_dir = argv[2];
}
memset (&file_descr, 0, sizeof (file_descr));
file_descr.write_block = test_write_chunk;
ret = filename_chunk_cdc (src_filename, &file_descr, NULL, TRUE);
if (ret == -1) {
fprintf(stderr, "file chunk failed\n");
exit(1);
}
ret = test_chunks (&file_descr);
if (ret < 0) {
fprintf (stderr, "chunk test failed.\n");
exit(1);
}
printf ("test passed.\n");
return 0;
}

105
tests/test-crypt.c Normal file
View File

@ -0,0 +1,105 @@
#include <glib.h>
#include <glib/gprintf.h>
#include <string.h>
#include "seafile-crypt.h"
#define CODE "this_is_user_passwd"
static int crypt_test (unsigned int len)
{
if (len <= 0) {
g_printf (" [%s] line %d: len must be positive.\n", __func__, __LINE__);
return -1;
}
char *msg = "Hello World!\n";
GString *gstr = g_string_new (NULL);
while (gstr->len < len) {
g_string_append (gstr, msg);
}
char *enc_out = NULL;
int enc_out_len;
g_printf ("[setup] The input is %d bytes\n", len);
int res = seafile_encrypt (&enc_out,
&enc_out_len,
gstr->str,
len,
CODE,
strlen(CODE));
if (res == 0 && enc_out_len != -1)
g_printf ("[ENC] [PASS] Encrypted output length is %d bytes\n", enc_out_len);
else {
g_printf ("[ENC] FAILED.\n");
goto error;
}
char *dec_out = NULL;
int dec_len;
res = seafile_decrypt (&dec_out,
&dec_len,
enc_out,
enc_out_len,
CODE,
strlen(CODE));
if (res != 0 || (unsigned int)dec_len != len ||
strncmp (dec_out, gstr->str, len) != 0) {
g_printf ("[DEC] FAILED.\n");
goto error;
}
else
g_printf ("[DEC] [PASS] Decrypted output is the totally same as input\n");
g_string_free (gstr, TRUE);
g_free (enc_out);
g_free (dec_out);
g_printf ("[TEST] Finished Successfully.\n");
return 0;
error:
g_string_free (gstr, TRUE);
g_free (enc_out);
g_free (dec_out);
g_printf ("[TEST] FAILED.\n");
return -1;
}
int main (void)
{
unsigned int len[7] = {1, 8, 16, 50, 111, 1111, 11111};
int i;
for (i = 0; i < 7; i ++) {
if (crypt_test (len[i]) != 0) {
g_printf ("TEST FAILED.\n");
return -1;
}
}
g_printf ("ALL TESTS FINISHED SUCCESSFULLY.\n");
return 0;
}

48
tests/test-index.c Normal file
View File

@ -0,0 +1,48 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "index.h"
static void rawdata_to_hex (const unsigned char *rawdata,
char *hex_str, int n_bytes)
{
static const char hex[] = "0123456789abcdef";
int i;
for (i = 0; i < n_bytes; i++) {
unsigned int val = *rawdata++;
*hex_str++ = hex[val >> 4];
*hex_str++ = hex[val & 0xf];
}
*hex_str = '\0';
}
int main (int argc, char *argv[])
{
char *index_file;
struct index_state istate;
if (argc < 2) {
fprintf (stderr, "%s index_file\n", argv[0]);
exit (-1);
}
index_file = argv[1];
memset (&istate, 0, sizeof(istate));
if (read_index_from (&istate, index_file) < 0) {
fprintf (stderr, "Corrupt index file %s\n", index_file);
exit (-1);
}
int i;
struct cache_entry *ce;
char id[41];
printf ("Totally %u entries in index.\n", istate.cache_nr);
for (i = 0; i < istate.cache_nr; ++i) {
ce = istate.cache[i];
rawdata_to_hex (ce->sha1, id, 20);
printf ("%s\t%d\t%s\n", ce->name, ce_stage(ce), id);
}
printf ("Index file format OK.\n");
return 0;
}

10
tests/test-rpc.py Normal file
View File

@ -0,0 +1,10 @@
import ccnet
import seafile
pool = ccnet.ClientPool("basic/conf1")
seafile_rpc = seafile.RpcClient(pool)
repos = seafile_rpc.get_repo_list("", 100)
for repo in repos:
print repo

98
tests/test-seafile-fmt.c Normal file
View File

@ -0,0 +1,98 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <openssl/sha.h>
#include "utils.h"
/* SHA1 calculate */
void
sha1( uint8_t * setme,
const void * content,
int content_len)
{
SHA_CTX sha;
SHA1_Init (&sha);
SHA1_Update (&sha, content, content_len);
SHA1_Final (setme, &sha);
}
int main (int argc, char **argv)
{
char *seafile_path, *file_path;
uint64_t file_size, block_size;
uint32_t n_blocks;
int fd;
FILE *fp;
if (argc != 3) {
printf ("seafile-fmt-test seafile original-file\n");
exit (1);
}
seafile_path = argv[1];
file_path = argv[2];
fd = g_open (seafile_path, O_RDONLY | O_BINARY);
if (fd < 0) {
printf ("Failed to open seafile.\n");
exit (1);
}
fp = g_fopen (file_path, "rb");
if (!fp) {
printf ("Failed to open original file.\n");
exit (1);
}
if (readn (fd, &file_size, sizeof(file_size)) < 0) {
printf ("Failed to read file size.\n");
exit (1);
}
if (readn (fd, &block_size, sizeof(block_size)) < 0) {
printf ("Failed to read block_size.\n");
exit (1);
}
n_blocks = (uint32_t) ((file_size + block_size -1) / block_size);
printf ("file size is %lld, block size is %lld, %d blocks.\n",
file_size, block_size, n_blocks);
uint8_t *blocks = (uint8_t *) malloc (n_blocks * 20);
if (readn (fd, blocks, n_blocks * 20) < n_blocks * 20) {
printf ("Failed to read blocks.\n");
exit (1);
}
int i;
char *block = valloc (block_size);
uint8_t sha1_buf[20];
for (i = 0; i < n_blocks; ++i) {
int n = fread (block, 1, block_size, fp);
if (n <= 0) {
printf ("Failed to read original file\n");
exit (1);
}
sha1 (sha1_buf, block, n);
if (memcmp (sha1_buf, blocks, 20) != 0) {
printf ("Error in blocks sha1\n");
exit (1);
}
blocks += 20;
}
printf ("Check OK\n");
return 0;
}

122
tests/test-share.py Executable file
View File

@ -0,0 +1,122 @@
#!/usr/bin/env python
from datetime import datetime
import os
import time
import shutil
import common
from common import CcnetDaemon, SeafileDaemon, print_cmsg, db_item_exists
import ccnet
from pysearpc import *
import seafile
def cleanup_and_exit():
os.system("""pkill ccnet""")
# os.system("""cd basic; ./clean.sh""")
exit()
ccnet_daemon1 = CcnetDaemon("basic/conf1")
ccnet_daemon1.start("--no-multicast")
ccnet_daemon2 = CcnetDaemon("basic/conf2")
ccnet_daemon2.start("--relay")
ccnet_daemon3 = CcnetDaemon("basic/conf3")
ccnet_daemon3.start("--no-multicast")
ccnet_daemon4 = CcnetDaemon("basic/conf4")
ccnet_daemon4.start("--no-multicast")
print_cmsg("Wait for ccnet daemon starting")
time.sleep(3)
if not os.access("basic/worktree", os.F_OK):
try:
os.mkdir("basic/worktree")
except OSError as e:
print_cmsg("Failed to create worktree: " + e.strerror)
cleanup_and_exit()
seaf_daemon1 = SeafileDaemon("basic/conf1")
seaf_daemon1.start("-w", "basic/worktree/wt1")
seaf_daemon2 = SeafileDaemon("basic/conf2")
seaf_daemon2.start("-r")
seaf_daemon3 = SeafileDaemon("basic/conf3")
seaf_daemon3.start("-w", "basic/worktree/wt3")
seaf_daemon4 = SeafileDaemon("basic/conf4")
seaf_daemon4.start("-w", "basic/worktree/wt4")
print_cmsg("sleep")
time.sleep(15)
os.system("""
cd basic;
./seafserv-tool -c conf2 add-server server
./seafserv-tool -c conf2 add-server server2
""")
pool1 = ccnet.ClientPool("basic/conf1")
ccnet_rpc1 = ccnet.CcnetRpcClient(pool1)
seaf_rpc1 = seafile.RpcClient(pool1)
seaf_rpc3 = seafile.RpcClient(ccnet.ClientPool("basic/conf3"))
repo_id = seaf_rpc1.create_repo("test-repo", "test")
if not repo_id:
print_cmsg("Failed to create repo")
cleanup_and_exit()
print_cmsg("Created repo " + repo_id)
print_cmsg("Copy data into basic/worktree/wt1")
try:
if not os.access("basic/worktree/wt1/%s/data" % repo_id, os.F_OK):
shutil.copytree("basic/data", "basic/worktree/wt1/%s/data" % repo_id)
except OSError as e:
print_cmsg("Failed to copy data: " + e.strerror)
cleanup_and_exit()
print_cmsg("Add and commit")
if seaf_rpc1.add(repo_id, "") < 0:
print_cmsg("Failed to add")
cleanup_and_exit()
if not seaf_rpc1.commit(repo_id, "commit1"):
print_cmsg("Failed to commit")
cleanup_and_exit()
print_cmsg("Get group id")
group_ids = ccnet_rpc1.list_groups()
if not group_ids:
print_cmsg("No group set up")
cleanup_and_exit()
test_group_id = ''
for group_id in group_ids.split("\n"):
if group_id == '':
break
group = ccnet_rpc1.get_group(group_id)
if group.props.name == "ccnet-dev":
test_group_id = group.props.id
break
if not test_group_id:
print_cmsg("Group ccnet-dev cannot be found")
cleanup_and_exit()
print_cmsg("Share %s to group %s" % (repo_id, test_group_id))
try:
if seaf_rpc1.share_repo(repo_id, test_group_id) < 0:
print_cmsg("Failed to share")
cleanup_and_exit()
except SearpcError as e:
print >>sys.stderr, "RPC error: %s" % str(e)
cleanup_and_exit()
print_cmsg("Wait for share info synchronized")
time.sleep(120)
share_info = seaf_rpc3.get_repo_sinfo(repo_id)
if not share_info:
print_cmsg("Failed to synchronize share info")
cleanup_and_exit()
cleanup_and_exit()

120
tests/test-transfer.py Executable file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env python
from datetime import datetime
import os
import time
import shutil
import common
from common import CcnetDaemon, SeafileDaemon, print_cmsg, db_item_exists
import ccnet
from pysearpc import *
import seafile
def cleanup_and_exit():
os.system("""pkill ccnet""")
# os.system("""cd basic; ./clean.sh""")
exit()
ccnet_daemon1 = CcnetDaemon("basic/conf1")
ccnet_daemon1.start("--no-multicast")
ccnet_daemon2 = CcnetDaemon("basic/conf2")
ccnet_daemon2.start("--relay")
ccnet_daemon3 = CcnetDaemon("basic/conf3")
ccnet_daemon3.start("--no-multicast")
ccnet_daemon4 = CcnetDaemon("basic/conf4")
ccnet_daemon4.start("--no-multicast")
print_cmsg("Wait for ccnet daemon starting")
time.sleep(3)
if not os.access("basic/worktree", os.F_OK):
try:
os.mkdir("basic/worktree")
except OSError as e:
print_cmsg("Failed to create worktree: " + e.strerror)
cleanup_and_exit()
seaf_daemon1 = SeafileDaemon("basic/conf1")
seaf_daemon1.start("-w", "basic/worktree/wt1")
seaf_daemon2 = SeafileDaemon("basic/conf2")
seaf_daemon2.start("-r")
seaf_daemon3 = SeafileDaemon("basic/conf3")
seaf_daemon3.start("-w", "basic/worktree/wt3")
seaf_daemon4 = SeafileDaemon("basic/conf4")
seaf_daemon4.start("-w", "basic/worktree/wt4")
print_cmsg("sleep")
time.sleep(15)
os.system("""
cd basic;
./seafserv-tool -c conf2 add-server server
./seafserv-tool -c conf2 add-server server2
""")
pool1 = ccnet.ClientPool("basic/conf1")
ccnet_rpc1 = ccnet.CcnetRpcClient(pool1)
seaf_rpc1 = seafile.RpcClient(pool1)
seaf_rpc3 = seafile.RpcClient(ccnet.ClientPool("basic/conf3"))
repo_id = seaf_rpc1.create_repo("test-repo", "test")
if not repo_id:
print_cmsg("Failed to create repo")
cleanup_and_exit()
print_cmsg("Created repo " + repo_id)
print_cmsg("Copy data into basic/worktree/wt1")
try:
if not os.access("basic/worktree/wt1/%s/data" % repo_id, os.F_OK):
shutil.copytree("basic/data", "basic/worktree/wt1/%s/data" % repo_id)
except OSError as e:
print_cmsg("Failed to copy data: " + e.strerror)
cleanup_and_exit()
print_cmsg("Add and commit")
try:
seaf_rpc1.add(repo_id, "")
except SearpcError as e:
print_cmsg("Failed to add: " + str(e))
cleanup_and_exit()
try:
seaf_rpc1.commit(repo_id, "commit1")
except SearpcError as e:
print_cmsg("Failed to commit: " + str(e))
cleanup_and_exit()
print_cmsg("Start upload")
try:
upload_tx_id = seaf_rpc1.upload(repo_id, "master", "master");
except SearpcError as e:
print_cmsg("Failed to start upload: " + str(e))
cleanup_and_exit()
print_cmsg("Wait for upload")
time.sleep(20)
try:
fetch_tx_id = seaf_rpc3.fetch(repo_id, "master", "master")
except SearpcError as e:
print_cmsg("Failed to start fetch: " + str(e))
cleanup_and_exit()
print_cmsg("Wait for fetch")
time.sleep(20)
print_cmsg("Initial checkout")
try:
seaf_rpc3.checkout(repo_id, "master")
except SearpcError as e:
print_cmsg("Failed to check out: " + str(e))
cleanup_and_exit()
seaf_rpc1.remove_task(upload_tx_id, 1)
seaf_rpc3.remove_task(fetch_tx_id, 0)
cleanup_and_exit()

11
tools/Makefile.am Normal file
View File

@ -0,0 +1,11 @@
#AM_CPPFLAGS = @GLIB2_CFLAGS@
bin_PROGRAMS = seaf-server-init
seaf_server_init_SOURCES = seaf-server-init.c ../common/seaf-db.c
seaf_server_init_LDADD = @GLIB2_LIBS@ @MYSQL_LIBS@ @ZDB_LIBS@
seaf_server_init_LDFLAGS = @STATIC_COMPILE@ @SERVER_PKG_RPATH@
seaf_server_init_CPPFLAGS = @GLIB2_CFLAGS@ @MYSQL_CFLAGS@ @ZDB_CFLAGS@

12
tools/README Normal file
View File

@ -0,0 +1,12 @@
Seafile Server SETUP GUIDE
==========================
Author: Seafile
Date: 2012-05-07 11:46:53 CST
1. Before you run the *setup-seafile.sh* script, MAKE SURE you have read the
server manual at [http://220.113.41.165/wiki/Server-manual] .
*Believe me*: Read the manual would save you much much time in setup and
configure the seafile server! Everything you need to know is there.

21
tools/conv-obj-store.py Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
import os
import sys
obj_dir = sys.argv[1]
obj_list = os.listdir(obj_dir)
for obj_id in obj_list:
old_path = os.path.join(obj_dir, obj_id)
l1_dir = os.path.join(obj_dir, obj_id[:2])
if not os.access(l1_dir, os.F_OK):
try:
os.mkdir(l1_dir)
except:
pass
new_path = os.path.join(l1_dir, obj_id[2:])
try:
os.rename(old_path, new_path)
except:
pass

44
tools/cp-setup-files.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
SCRIPT=$(readlink -f "$0")
SCRIPT_PATH=$(dirname "${SCRIPT}")
scriptname=$0
function usage () {
printf "\nUsage: ${scriptname} <server-pkg-dir>\n\n"
}
if [[ $# != 1 ]]; then
usage
exit 1
fi
destdir=$1
if [[ ! -d ${destdir} ]] ; then
printf "\n\"${destdir}\" is not a valid directory\n\n"
exit 1
fi
if [[ ! -d ${destdir}/seafile || ! -d ${destdir}/seahub ]]; then
echo "You should have seafile and seahub dir under ${destdir}"
exit 1
fi
scripts="setup-seafile.sh seafile.sh seahub.sh"
for file in ${scripts} ; do
cp -f "${SCRIPT_PATH}/${file}" "${destdir}"
done
runtime_dir=${destdir}/runtime
if [[ ! -d "${runtime_dir}" ]]; then
mkdir -p "${runtime_dir}"
fi
cp -f "${SCRIPT_PATH}/seahub.conf" "${runtime_dir}"
# copy html files used by seaf-httpserver
cp -f -a "${SCRIPT_PATH}/../server/httpserver/htmls" "${runtime_dir}"
printf "Done.\n\n"

126
tools/create-seahub-admin.sh Executable file
View File

@ -0,0 +1,126 @@
#!/bin/bash
SCRIPT=$(readlink -f "$0")
INSTALLPATH=$(dirname "${SCRIPT}")
TOPDIR=$(dirname "${INSTALLPATH}")
default_ccnet_conf_dir=${TOPDIR}/ccnet
default_seafile_data_dir=${TOPDIR}/seafile-data
default_seahub_db=${TOPDIR}/seahub.db
function welcome () {
echo
echo "-----------------------------------------------------------------"
echo "This script will help you create a seahub admin account."
echo "press [ENTER] to continue"
echo "-----------------------------------------------------------------"
read dummy
echo ""
}
function err_and_quit () {
printf "\n\n\033[33mError occured during execution. \nPlease fix possible problems and run the script again.\033[m\n\n"
}
function check_dependency () {
if ! which sqlite3 2>/dev/null 1>&2; then
printf "\033[33msqlite3\033[m is not installed, install it first"
exit 1;
fi
}
function ask_question () {
question=$1
default=$2
key=$3
printf "${question}"
printf "\n"
if [[ "${default}" != "" && "${default}" != "nodefault" ]] ; then
printf "[default: ${default} ] "
elif [[ "${key}" != "" ]]; then
printf "[${key}]: "
fi
}
function get_seahub_admin_email () {
question="Please specify the email address for seahub admininstrator:"
ask_question "${question}" "nodefault" "seahub admin email"
read seahub_admin_email
if [[ "${seahub_admin_email}" == "" ]]; then
echo "Seahub admin user name can't be empty."
get_seahub_admin_email;
elif [[ ! ${seahub_admin_email} =~ ^.+@.*\..+$ ]]; then
echo "${seahub_admin_email} is not a valid email address"
get_seahub_admin_email;
fi
}
function get_seahub_admin_passwd () {
echo ""
question="Please specify the passwd you want to use for seahub admininstrator:"
ask_question "${question}" "nodefault" "seahub admin password"
read seahub_admin_passwd
echo ""
question="Please ensure the passwd again:"
ask_question "${question}" "nodefault" "seahub admin password again"
read seahub_admin_passwd_again
echo ""
if [[ "${seahub_admin_passwd}" != "${seahub_admin_passwd_again}" ]]; then
printf "\033[33mTwo passwords you give mismatch.\033[m"
get_seahub_admin_passwd;
elif [[ "${seahub_admin_passwd}" == "" ]]; then
echo "Passwords can't be empty."
get_seahub_admin_passwd;
fi
}
welcome;
check_dependency;
get_seahub_admin_email;
sleep .5;
get_seahub_admin_passwd;
seahub_admin_passwd_enc=$(echo -n ${seahub_admin_passwd} | sha1sum | grep -o "[0-9a-f]*")
sleep .5;
printf "\n\n"
echo "This is your seahub admin username/password"
echo ""
printf "admin user name: \033[33m${seahub_admin_email}\033[m\n"
printf "admin password: \033[33m${seahub_admin_passwd}\033[m\n\n"
echo ""
echo "If you are OK with these configuration, press [ENTER] to continue."
read dummy
usermgr_db_dir=${default_ccnet_conf_dir}/PeerMgr/
usermgr_db=${usermgr_db_dir}/usermgr.db
# create admin user/passwd entry in ccnet db
if ! mkdir -p "${usermgr_db_dir}" 2>/dev/null 1>&2 ; then
echo "Failed to create seahub admin."
err_and_quit;
fi
sql="CREATE TABLE IF NOT EXISTS EmailUser (id INTEGER NOT NULL PRIMARY KEY, email TEXT, passwd TEXT, is_staff bool NOT NULL, is_active bool NOT NULL, ctime INTEGER)";
if ! sqlite3 "${usermgr_db}" "${sql}" ; then
rm -f "${usermgr_db}"
echo "Failed to create seahub admin."
err_and_quit;
fi
sql="INSERT INTO EmailUser(email, passwd, is_staff, is_active, ctime) VALUES (\"${seahub_admin_email}\", \"${seahub_admin_passwd_enc}\", 1, 1, 0);"
if ! sqlite3 "${usermgr_db}" "${sql}" ; then
rm -f "${usermgr_db}"
echo "Failed to create seahub admin."
err_and_quit;
fi
# final message
echo ""
echo "Successfully created a seahub admin account for you."
echo ""

35
tools/pack-server.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
function usage () {
echo
echo "Usage: pack-server.sh <version>"
echo
}
if [[ $# != 1 ]]; then
usage;
exit 1;
fi
version=$1
if [[ ! ${version} =~ [0-9]\.[0-9]\.[0-9] ]]; then
echo
echo "\"${version}\" is not a invalid version number"
echo "a valid version is like 0.9.3."
echo
exit 1;
fi
server_dir=seafile-server-${version}
if [[ ! -d ${server_dir} ]]; then
echo
echo "The directory \"${server_dir}\" does not exist"
echo
exit 1;
fi
serverpath=seafile-server-${version}
tar czhvf seafile-server_${version}_x86-64.tar.gz ${serverpath} --exclude-vcs --exclude=${serverpath}/seafile/share* --exclude=${serverpath}/seafile/include* --exclude=${serverpath}/runtime/*.log --exclude=${serverpath}/runtime/*.pid --exclude=${serverpath}/seahub/seahub.db --exclude=${serverpath}/seahub/avatar/testdata

219
tools/seaf-server-init.c Normal file
View File

@ -0,0 +1,219 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <glib.h>
#include "../common/seaf-db.h"
static int
save_config_file (GKeyFile *key_file, const char *path)
{
GError *error = NULL;
char *config = g_key_file_to_data (key_file, NULL, &error);
if (error) {
fprintf (stderr, "Failed to save config file to %s: %s\n",
path, error->message);
return -1;
}
FILE *fp = fopen (path, "w");
if (fp == NULL) {
fprintf (stderr, "Failed to save config file: %s %s.\n",
path, strerror(errno));
return -1;
}
fputs (config, fp);
fclose (fp);
return 0;
}
static const char *short_opts = "hmvs:t:r:d:p:";
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "verbose", no_argument, NULL, 'v' },
{ "mysql", no_argument, NULL, 'm' },
{ "host", required_argument, NULL, 's' },
{ "socket", required_argument, NULL, 't' },
{ "root-passwd", required_argument, NULL, 'r' },
{ "seafile-dir", required_argument, NULL, 'd' },
{ "port", required_argument, NULL, 'p' },
{ 0, 0, 0, 0 },
};
struct seaf_server_config {
char *mysql_host;
char *mysql_root_passwd;
char *mysql_db_name;
char *mysql_seahub_db_name;
char *seafile_dir;
char *mysql_socket;
char *port;
};
static struct seaf_server_config config = {
"localhost",
NULL,
"seafile-meta",
"seahub-meta",
NULL,
NULL,
"12001",
};
void usage(int code) {
fprintf (stderr,
"\nUsage: seaf-server-init [OPTIONS]\n"
"Initialize your seafile server configuration\n\n"
"Required arguments are:\n\n"
" -h, --help output help and quit\n"
" -v, --verbose output more information\n"
" -d, --seafile-dir specify a diretory to put your seafile server config and data\n"
" -p, --port specify a port to to transmit data\n"
" -m, --mysql use mysql database. Default is to use sqlite3\n"
"\n"
"When using mysql database, you need to specify these arguments:\n\n"
" -r, --root-passwd your mysql root passwd, needed to create seafile server database\n"
" -s, --host Optional. Your mysql server host name, default is localhost\n"
" -t, --socket Optional. Your mysql server socket path.\n"
"\n"
);
exit(code);
}
int main (int argc, char **argv)
{
char sql[256];
gboolean verbose = FALSE;
gboolean use_mysql = FALSE;
int ret;
if (argc == 1)
usage(1);
int c;
while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != EOF) {
switch (c) {
case 'h':
usage(0);
break;
case 'v':
verbose = TRUE;
break;
case 'm':
use_mysql = TRUE;
break;
case 's':
config.mysql_host = strdup(optarg);
break;
case 't':
config.mysql_socket = strdup(optarg);
break;
case 'r':
config.mysql_root_passwd = strdup(optarg);
break;
case 'd':
config.seafile_dir = strdup(optarg);
break;
case 'p':
config.port = strdup(optarg);
break;
default:
usage(1);
}
}
if (use_mysql && !config.mysql_root_passwd) {
fprintf (stderr, "You choose to use mysql database. "
"Mysql Root Password must be specified.\n");
exit(1);
}
if (!config.seafile_dir) {
fprintf (stderr, "You must specify seafile data dir\n");
usage(1);
}
/* Create database for mysql */
if (use_mysql) {
SeafDB *db_root = seaf_db_new_mysql (config.mysql_host, "root",
config.mysql_root_passwd,
NULL, config.mysql_socket);
if (!db_root) {
fprintf (stderr, "Out of memory!\n");
return 1;
}
/* Create database for Seafile server. */
snprintf (sql, sizeof(sql), "CREATE DATABASE IF NOT EXISTS `%s`",
config.mysql_db_name);
ret = seaf_db_query (db_root, sql);
if (ret < 0) {
fprintf (stderr, "Failed to create database %s.\n", config.mysql_db_name);
return 1;
}
if (verbose)
printf ("Successfully created database: %s\n",
config.mysql_db_name);
/* Create database for Seahub. */
snprintf (sql, sizeof(sql), "CREATE DATABASE IF NOT EXISTS `%s` character set utf8",
config.mysql_seahub_db_name);
ret = seaf_db_query (db_root, sql);
if (ret < 0) {
fprintf (stderr, "Failed to create database %s.\n",
config.mysql_seahub_db_name);
return 1;
}
if (verbose)
printf ("Successfully created database: %s\n",
config.mysql_seahub_db_name);
}
/* Generate config file. */
GKeyFile *key_file = g_key_file_new ();
g_key_file_set_string (key_file, "database", "type",
use_mysql ? "mysql" : "sqlite");
if (use_mysql) {
g_key_file_set_string (key_file, "database", "host", config.mysql_host);
g_key_file_set_string (key_file, "database", "user", "root");
g_key_file_set_string (key_file, "database", "password", config.mysql_root_passwd);
g_key_file_set_string (key_file, "database", "db_name", config.mysql_db_name);
if (config.mysql_socket)
g_key_file_set_string (key_file, "database", "unix_socket", config.mysql_socket);
}
g_key_file_set_string (key_file, "network", "port", config.port);
struct stat st;
if (lstat (config.seafile_dir, &st) < 0) {
if (mkdir (config.seafile_dir, 0777) < 0) {
fprintf (stderr, "Directory %s cannot be created.\n", config.seafile_dir);
return 1;
}
}
char *seafile_conf = g_build_filename (config.seafile_dir, "seafile.conf", NULL);
if (verbose)
printf ("Generating config files: %s\n", seafile_conf);
if (save_config_file (key_file, seafile_conf) < 0)
return 1;
printf ("Done.\n");
return 0;
}

119
tools/seafile.sh Executable file
View File

@ -0,0 +1,119 @@
#!/bin/bash
echo ""
SCRIPT=$(readlink -f "$0")
INSTALLPATH=$(dirname "${SCRIPT}")
TOPDIR=$(dirname "${INSTALLPATH}")
default_ccnet_conf_dir=${TOPDIR}/ccnet
ccnet_pidfile=${INSTALLPATH}/runtime/ccnet.pid
export LD_LIBRARY_PATH=${INSTALLPATH}/seafile/lib/:${LD_LIBRARY_PATH}
script_name=$0
function usage () {
echo "usage : "
echo "$(basename ${script_name}) { start | stop | restart } "
echo ""
}
# check args
if [[ $# != 1 || ( "$1" != "start" && "$1" != "stop" && "$1" != "restart" ) ]]; then
usage;
exit 1;
fi
function validate_ccnet_conf_dir () {
if [[ ! -d ${default_ccnet_conf_dir} ]]; then
echo "Error: there is no ccnet config directory."
echo "Have you run setup-seafile.sh before this?"
echo ""
exit -1;
fi
}
function read_seafile_data_dir () {
seafile_ini=${default_ccnet_conf_dir}/seafile.ini
if [[ ! -f ${seafile_ini} ]]; then
echo "${seafile_ini} not found. Now quit"
exit 1
fi
seafile_data_dir=$(cat "${seafile_ini}")
if [[ ! -d ${seafile_data_dir} ]]; then
echo "Your seafile server data directory \"${seafile_data_dir}\" is invalid or doesn't exits."
echo "Please check it first, or create this directory yourself."
echo ""
exit 1;
fi
}
function validate_alreay_running () {
if pgrep -f "seafile-controller -c ${default_ccnet_conf_dir}" 2>/dev/null 1>&2; then
echo "Seafile server already running."
exit 1;
fi
}
function start_seafile_server () {
validate_alreay_running;
validate_ccnet_conf_dir;
read_seafile_data_dir;
echo "Starting seafile server, please wait ..."
seaf_controller="${INSTALLPATH}/seafile/bin/seafile-controller"
httpserver="${INSTALLPATH}/seafile/bin/httpserver"
bin_dir="${INSTALLPATH}/seafile/bin"
htmls_dir="${INSTALLPATH}/runtime/htmls"
${seaf_controller} -c "${default_ccnet_conf_dir}" -d "${seafile_data_dir}" \
-b "${bin_dir}"
sleep 3
${httpserver} -c "${default_ccnet_conf_dir}" -d "${seafile_data_dir}" -r "${htmls_dir}"
echo "Seafile server started"
}
function stop_seafile_server () {
if ! pgrep -f "seafile-controller -c ${default_ccnet_conf_dir}" 2>/dev/null 1>&2; then
echo "seafile server not running yet"
return 1;
fi
echo "Stopping seafile server ..."
pkill -SIGTERM -f "seafile-controller -c ${default_ccnet_conf_dir}"
pkill -f "httpserver -c ${default_ccnet_conf_dir}"
return 0
}
function restart_seafile_server () {
stop_seafile_server;
sleep 2
start_seafile_server;
}
manage_py=${INSTALLPATH}/seahub/manage.py
function check_seahub_running () {
if pgrep -f "${manage_py} run_gunicorn" 2>/dev/null 1>&2; then
echo "Seahub is running, please stop it before stop seafile."
printf "You can stop it by \"\033[33m./seahub.sh stop\033[m\"\n\n"
exit 1;
fi
}
case $1 in
"start" )
start_seafile_server;
;;
"stop" )
check_seahub_running;
stop_seafile_server;
;;
"restart" )
restart_seafile_server;
esac
echo "Done."

12
tools/seahub.conf Normal file
View File

@ -0,0 +1,12 @@
import os
daemon = True
workers = 3
# Logging
runtime_dir = os.path.dirname(__file__)
pidfile = os.path.join(runtime_dir, 'seahub.pid')
errorlog = os.path.join(runtime_dir, 'error.log')
accesslog = os.path.join(runtime_dir, 'access.log')
# for file upload, we need a longer timeout value (default is only 30s, too short)
timeout = 1200

124
tools/seahub.sh Executable file
View File

@ -0,0 +1,124 @@
#!/bin/bash
echo ""
SCRIPT=$(readlink -f "$0")
INSTALLPATH=$(dirname "${SCRIPT}")
TOPDIR=$(dirname "${INSTALLPATH}")
default_ccnet_conf_dir=${TOPDIR}/ccnet
manage_py=${INSTALLPATH}/seahub/manage.py
gunicorn_conf=${INSTALLPATH}/runtime/seahub.conf
gunicorn_pidfile=${INSTALLPATH}/runtime/seahub.pid
export LD_LIBRARY_PATH=${INSTALLPATH}/seafile/lib/:${LD_LIBRARY_PATH}
script_name=$0
function usage () {
echo "Usage : "
echo "$(basename ${script_name}) { start <port> | stop | restart <port> }"
echo "default port is 8000"
echo ""
}
# Check args
if [[ $1 != "start" && $1 != "stop" && $1 != "restart" ]]; then
usage;
exit 1;
fi
function validate_seaf_server_running () {
if ! pgrep -f "seafile-controller -c ${default_ccnet_conf_dir}" 2>/dev/null 1>&2; then
echo "seafile server process is not running, please start it by:"
echo ""
echo " ./seafile.sh start"
echo ""
exit 1;
fi
}
function validate_seahub_running () {
if pgrep -f "${manage_py} run_gunicorn" 2>/dev/null 1>&2; then
echo "Seahub is already running."
exit 1;
fi
}
function validate_port () {
if ! [[ ${port} =~ ^[1-9][0-9]{1,4}$ ]] ; then
printf "\033[033m${port}\033[m is not a valid port number\n\n"
usage;
exit 1
fi
}
if [[ ($1 == "start" || $1 == "restart") && ($# == 2 || $# == 1) ]]; then
if [[ $# == 2 ]]; then
port=$2
validate_port
else
port=8000
fi
elif [[ $1 == "stop" && $# == 1 ]]; then
dummy=dummy
else
usage;
exit 1
fi
function start_seahub () {
validate_seaf_server_running;
validate_seahub_running;
pid=$(pgrep -f "${manage_py} run_gunicorn")
if [[ "${pid}" != "" ]]; then
echo "Seahub has already been running.".
exit 1;
fi
echo "Starting seahub http server at port ${port} ..."
seafile_python_path=${INSTALLPATH}/seafile/lib/python2.7/site-packages
thirdpart_path=${INSTALLPATH}/seahub/thirdpart
export CCNET_CONF_DIR=${default_ccnet_conf_dir}
export PYTHONPATH=${seafile_python_path}:${thirdpart_path}
"${manage_py}" run_gunicorn -c "${gunicorn_conf}" -b "0.0.0.0:${port}"
# Ensure seahub is started successfully
sleep 2
if ! pgrep -f "${manage_py} run_gunicorn" 2>/dev/null 1>&2; then
printf "\033[33mError:Seahub failed to start.\033[m\n"
echo "Please try to run \"./seafile.sh start\" again"
echo "If it fails again, Please remove ${default_ccnet_conf_dir} and run ./setup-seafile.sh again"
exit 1;
fi
}
function stop_seahub () {
if [[ -f ${gunicorn_pidfile} ]]; then
pid=$(cat "${gunicorn_pidfile}")
echo "Stopping seahub ..."
kill ${pid}
return 0
else
echo "Seahub is not running"
fi
}
function restart_seahub () {
stop_seahub
sleep 2
start_seahub
}
case $1 in
"start" )
start_seahub;
;;
"stop" )
stop_seahub;
;;
"restart" )
restart_seahub;
;;
esac
echo "Done."
echo ""

492
tools/setup-seafile.sh Executable file
View File

@ -0,0 +1,492 @@
#!/bin/bash
SCRIPT=$(readlink -f "$0")
INSTALLPATH=$(dirname "${SCRIPT}")
TOPDIR=$(dirname "${INSTALLPATH}")
default_ccnet_conf_dir=${TOPDIR}/ccnet
default_seafile_data_dir=${TOPDIR}/seafile-data
default_seahub_db=${TOPDIR}/seahub.db
old_ld_path=$LD_LIBRARY_PATH
new_ld_path=${INSTALLPATH}/seafile/lib/:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=$new_ld_path
use_existing_ccnet="false"
use_existing_seafile="false"
server_manual_http="http://wiki.seafile.com.cn/wiki/Server-manual"
function welcome () {
echo "-----------------------------------------------------------------"
echo "This script will guide you to config and setup your seafile server."
echo -e "\nMake sure you have read seafile server manual at \n\n\t${server_manual_http}\n"
echo "Press [ENTER] to continue"
echo "-----------------------------------------------------------------"
read dummy
echo
}
function err_and_quit () {
printf "\n\n\033[33mError occured during setup. \nPlease fix possible problems and run the script again.\033[m\n\n"
exit 1;
}
function on_ctrl_c_pressed () {
printf "\n\n\033[33mYou have pressed Ctrl-C. Setup is interrupted.\033[m\n\n"
exit 1;
}
# clean newly created ccnet/seafile configs when exit on SIGINT
trap on_ctrl_c_pressed 2
function check_sanity () {
if ! [[ -d ${INSTALLPATH}/seahub && -d ${INSTALLPATH}/seafile \
&& -d ${INSTALLPATH}/runtime ]]; then
echo
echo "The seafile-server diretory doesn't contain all needed files."
echo "Please make sure you have extracted all files and folders from tarball."
err_and_quit;
fi
}
function read_yes_no () {
printf "[yes|no] "
read yesno;
while [[ "${yesno}" != "yes" && "${yesno}" != "no" ]]
do
printf "please answer [yes|no] "
read yesno;
done
if [[ "${yesno}" == "no" ]]; then
return 1;
else
return 0;
fi
}
function check_root () {
# -------------------------------------------
# If running as root, ask the user to ensure it.
# -------------------------------------------
username="$(whoami)"
if [[ "${username}" == "root" ]]; then
echo
echo "You are running this script as ROOT. Are you sure to continue?"
if ! read_yes_no; then
echo "You should re-run this script as non-root user."
echo
exit 1;
fi
echo
fi
}
function check_existing_ccnet () {
if [[ -d ${default_ccnet_conf_dir} ]]; then
echo "It seems you have created a ccnet configuration before. "
echo "Do you want to use the existing configuration?"
if ! read_yes_no; then
echo
echo "Please remove the existing configuration before continue."
echo "You can do it by \"rm -rf ${default_ccnet_conf_dir}\""
echo
exit 1;
else
echo
echo "Existing ccnet configuration would be used."
use_existing_ccnet=true
fi
fi
echo
}
function check_python_module () {
module=$1
name=$2
hint=$3
printf " Checking python module: ${name} ... "
if ! python -c "import ${module}" 2>/dev/null 1>&2; then
echo
printf "\033[33m ${name} \033[m is not installed, Please install it first.\n"
if [[ "${hint}" != "" ]]; then
printf "${hint}"
echo
fi
err_and_quit;
fi
echo -e "Done."
}
function check_python () {
echo "Checking python on this machine ..."
if ! which python 2>/dev/null 1>&2; then
echo "No python found on this machine. Please install it first."
err_and_quit;
else
if (python --version 2>&1 | grep "3\\.[0-9].\\.[0-9]") 2>/dev/null 1>&2 ; then
printf "\033[33m Python version 3.x \033[m detected\n"
echo "Python 3.x is not supported. Please use python 2.x. Now quit."
err_and_quit;
fi
hint="\nOn Debian/Ubntu: apt-get install python-setuptools\nOn CentOS/RHEL: yum install python-setuptools"
check_python_module pkg_resources setuptools "${hint}"
hint="\nOn Debian/Ubntu: apt-get install python-simplejson\nOn CentOS/RHEL: yum install python-simplejson"
check_python_module simplejson python-simplejson "${hint}"
hint="\nOn Debian/Ubntu: apt-get install python-imaging\nOn CentOS/RHEL: yum install python-imaging"
check_python_module PIL python-imaging "${hint}"
fi
echo
}
function check_sqlite3 () {
echo -n "Checking for sqlite3 ..."
if ! which sqlite3 2>/dev/null 1>&2; then
echo -e "\nSqlite3 is not found. install it first.\n"
echo "On Debian/Ubuntu: apt-get install sqlite3"
echo "On CentOS/RHEL: yum install sqlite"
err_and_quit;
fi
printf "Done.\n\n"
}
function check_system_dependency () {
printf "Checking packages needed by seafile ...\n\n"
check_python;
check_sqlite3;
printf "Checking Done.\n\n"
}
function ask_question () {
question=$1
default=$2
key=$3
printf "${question}"
printf "\n"
if [[ "${default}" != "" && "${default}" != "nodefault" ]] ; then
printf "[default: ${default} ] "
elif [[ "${key}" != "" ]]; then
printf "[${key}]: "
fi
}
function get_server_name () {
question="What do you want to use as the name of this seafile server?\nYour seafile users would see this name in their seafile client."
hint="You can use a-z, A-Z, 0-9, _ and -, and the length should be 3 ~ 15"
ask_question "${question}\n${hint}" "nodefault" "server name"
read server_name
if [[ "${server_name}" == "" ]]; then
echo
echo "server name can not be empty"
get_server_name
elif [[ ! ${server_name} =~ ^[a-zA-Z][a-zA-Z0-9_-]{2,14}$ ]]; then
printf "\n\033[33m${server_name}\033[m is not a valid name.\n"
get_server_name;
fi
echo
}
function get_server_ip_or_domain () {
question="What is the ip or domain of this server?\nFor example, www.mycompany.com, or, 192.168.1.101"
ask_question "${question}\n" "nodefault" "ip or domain"
read ip_or_domain
if [[ "${ip_or_domain}" == "" ]]; then
echo
echo "ip or domain can not be empty"
get_server_ip_or_domain
fi
echo
}
function get_server_port () {
question="What tcp port do you want to use for seafile server?"
hint="10001 is the recommended port."
default="10001"
ask_question "${question}\n${hint}" "${default}"
read server_port
if [[ "${server_port}" == "" ]]; then
server_port="${default}"
fi
if [[ ! ${server_port} =~ ^[0-9]+$ ]]; then
echo "\"${server_port}\" is not a valid port number. "
get_server_port
fi
echo
}
function get_seafile_data_dir () {
question="Where do you want to put your seafile data?"
note="The size of seafile data diretory would increase very large, please use a volume with enough free space."
default=${default_seafile_data_dir}
ask_question "${question} \n\033[33mNote: \033[m${note}" "${default}"
read seafile_data_dir
if [[ "${seafile_data_dir}" == "" ]]; then
seafile_data_dir=${default}
fi
if [[ -d ${seafile_data_dir} && -f ${seafile_data_dir}/seafile.conf ]]; then
echo
echo "It seems you have existing seafile data in ${seafile_data_dir}."
echo "Do you want to use the existing seafile data?"
if ! read_yes_no; then
echo "You choose not to use existing seafile data in ${seafile_data_dir}"
echo "You need to specify another seafile data directory , or remove ${seafile_data_dir} before continue."
get_seafile_data_dir
else
use_existing_seafile="true"
fi
elif [[ -d ${seafile_data_dir} ]]; then
echo
echo "${seafile_data_dir} is an existing directory. Please specify another directory"
echo
get_seafile_data_dir
elif [[ ! ${seafile_data_dir} =~ ^/ ]]; then
echo
echo "\"${seafile_data_dir}\" is not an absolute path. Please specify an absolute path."
echo
get_seafile_data_dir
elif [[ ! -d $(dirname ${seafile_data_dir}) ]]; then
echo
echo "The path $(dirname ${seafile_data_dir}) does not exist."
echo
get_seafile_data_dir
fi
echo
}
# -------------------------------------------
# Main workflow of this script
# -------------------------------------------
check_root;
sleep .5
check_sanity;
welcome;
sleep .5
check_system_dependency;
sleep .5
check_existing_ccnet;
if [[ ${use_existing_ccnet} != "true" ]]; then
get_server_name;
get_server_ip_or_domain;
get_server_port;
fi
get_seafile_data_dir;
sleep .5
printf "\nThis is your config information:\n\n"
if [[ ${use_existing_ccnet} != "true" ]]; then
printf "server name: \033[33m${server_name}\033[m\n"
printf "server ip/domain: \033[33m${ip_or_domain}\033[m\n"
printf "server port: \033[33m${server_port}\033[m\n"
else
printf "ccnet config: use existing config in \033[33m${default_ccnet_conf_dir}\033[m\n"
fi
if [[ ${use_existing_seafile} != "true" ]]; then
printf "seafile data dir: \033[33m${seafile_data_dir}\033[m\n"
else
printf "seafile data dir: use existing data in \033[33m${seafile_data_dir}\033[m\n"
fi
echo
echo "If you are OK with these configuration, press [ENTER] to continue."
read dummy
ccnet_init=${INSTALLPATH}/seafile/bin/ccnet-init
seaf_server_init=${INSTALLPATH}/seafile/bin/seaf-server-init
# -------------------------------------------
# Create ccnet conf
# -------------------------------------------
if [[ "${use_existing_ccnet}" != "true" ]]; then
echo "Generating ccnet configuration in ${default_ccnet_conf_dir}..."
echo
if ! "${ccnet_init}" -c "${default_ccnet_conf_dir}" --name "${server_name}" \
--port "${server_port}" --host "${ip_or_domain}" 2>/dev/null 1>&2 ; then
err_and_quit;
fi
echo "Done. "
echo
fi
sleep 0.5
# -------------------------------------------
# Create seafile conf
# -------------------------------------------
if [[ "${use_existing_seafile}" != "true" ]]; then
echo "Generating seafile configuration in ${seafile_data_dir} ..."
echo
if ! ${seaf_server_init} --seafile-dir "${seafile_data_dir}" 2>/dev/null 1>&2; then
echo "Failed to generate seafile configuration"
err_and_quit;
fi
echo "Done. "
echo
fi
# -------------------------------------------
# Write seafile.ini
# -------------------------------------------
echo "${seafile_data_dir}" > "${default_ccnet_conf_dir}/seafile.ini"
# -------------------------------------------
# generate seahub/settings.py
# -------------------------------------------
dest_settings_py=${TOPDIR}/seahub_settings.py
seahub_secret_keygen=${INSTALLPATH}/seahub/tools/secret_key_generator.py
HTTP_SERVER_ROOT=http://${ip_or_domain}:8082
if [[ ! -f ${dest_settings_py} ]]; then
echo "HTTP_SERVER_ROOT = \"${HTTP_SERVER_ROOT}\"" > "${dest_settings_py}"
echo -n "SECRET_KEY = " >> "${dest_settings_py}"
key=$(python "${seahub_secret_keygen}")
echo "\"${key}\"" >> "${dest_settings_py}"
fi
# -------------------------------------------
# Seahub related config
# -------------------------------------------
echo "-----------------------------------------------------------------"
echo "Seahub is the web server for seafile server administaration."
echo "Now let's setup seahub configuration. Press [ENTER] to continue"
echo "-----------------------------------------------------------------"
echo
read dummy
echo "Please specify the email address and password for seahub admininstrator."
echo "You would use them to login as admin on your seahub website."
echo
function get_seahub_admin_email () {
question="Please specify the email address for seahub admininstrator:"
ask_question "${question}" "nodefault" "seahub admin email"
read seahub_admin_email
if [[ "${seahub_admin_email}" == "" ]]; then
echo "Seahub admin user name can't be empty."
get_seahub_admin_email;
elif [[ ! ${seahub_admin_email} =~ ^.+@.*\..+$ ]]; then
echo "${seahub_admin_email} is not a valid email address"
get_seahub_admin_email;
fi
}
function get_seahub_admin_passwd () {
echo
question="Please specify the passwd you want to use for seahub admininstrator:"
ask_question "${question}" "nodefault" "seahub admin password"
read seahub_admin_passwd
echo
question="Please ensure the passwd again:"
ask_question "${question}" "nodefault" "seahub admin password again"
read seahub_admin_passwd_again
echo
if [[ "${seahub_admin_passwd}" != "${seahub_admin_passwd_again}" ]]; then
printf "\033[33mTwo passwords you give mismatch.\033[m"
get_seahub_admin_passwd;
elif [[ "${seahub_admin_passwd}" == "" ]]; then
echo "Passwords can't be empty."
get_seahub_admin_passwd;
fi
}
get_seahub_admin_email;
sleep .5;
get_seahub_admin_passwd;
seahub_admin_passwd_enc=$(echo -n ${seahub_admin_passwd} | sha1sum | grep -o "[0-9a-f]*")
sleep .5;
printf "\n\n"
echo "This is your seahub admin username/password"
echo
printf "admin user name: \033[33m${seahub_admin_email}\033[m\n"
printf "admin password: \033[33m${seahub_admin_passwd}\033[m\n\n"
echo
echo "If you are OK with these configuration, press [ENTER] to continue."
read dummy
usermgr_db_dir=${default_ccnet_conf_dir}/PeerMgr/
usermgr_db=${usermgr_db_dir}/usermgr.db
if [[ "${use_existing_ccnet}" != "true" ]]; then
export LD_LIBRARY_PATH=$old_ld_path
# create admin user/passwd entry in ccnet db
if ! mkdir -p "${usermgr_db_dir}" 2>/dev/null 1>&2 ; then
echo "Failed to create seahub admin."
err_and_quit;
fi
sql="CREATE TABLE IF NOT EXISTS EmailUser (id INTEGER NOT NULL PRIMARY KEY, email TEXT, passwd TEXT, is_staff bool NOT NULL, is_active bool NOT NULL, ctime INTEGER)";
if ! sqlite3 "${usermgr_db}" "${sql}" ; then
rm -f "${usermgr_db}"
echo "Failed to create seahub admin."
err_and_quit;
fi
sql="INSERT INTO EmailUser(email, passwd, is_staff, is_active, ctime) VALUES (\"${seahub_admin_email}\", \"${seahub_admin_passwd_enc}\", 1, 1, 0);"
if ! sqlite3 "${usermgr_db}" "${sql}" ; then
rm -f "${usermgr_db}"
echo "Failed to create seahub admin."
err_and_quit;
fi
export LD_LIBRARY_PATH=$new_ld_path
fi
printf "Now sync seahub database ... "
export PYTHONPATH=${INSTALLPATH}/seafile/lib/python2.7/site-packages:${INSTALLPATH}/seahub/thirdpart:${PYTHONPATH}
manage_py=${INSTALLPATH}/seahub/manage.py
pushd "${INSTALLPATH}/seahub" 2>/dev/null 1>&2
if ! python manage.py syncdb 2>/dev/null 1>&2; then
popd 2>/dev/null 1>&2
echo "Failed to sync seahub database."
err_and_quit;
fi
popd 2>/dev/null 1>&2
printf "Done.\n"
# prepare avatar folder
media_dir=${INSTALLPATH}/seahub/media
orig_avatar_dir=${INSTALLPATH}/seahub/media/avatars
dest_avatar_dir=${TOPDIR}/seahub-data/avatars
if [[ ! -d ${dest_avatar_dir} ]]; then
mkdir -p "${TOPDIR}/seahub-data"
mv "${orig_avatar_dir}" "${dest_avatar_dir}"
ln -s ../../../seahub-data/avatars ${media_dir}
fi
# -------------------------------------------
# final message
# -------------------------------------------
sleep 1
echo
echo "-----------------------------------------------------------------"
echo "Your seafile server configuration has been finished successfully."
echo "-----------------------------------------------------------------"
echo
echo "run seafile server: ./seafile.sh { start | stop | restart }"
echo "run seahub server: ./seahub.sh { start <port> | stop | restart <port> } "
echo
echo -e "When problems occur, Refer to\n"
echo -e " ${server_manual_http}\n"
echo "for information."
echo

91
tools/upgrade-server.sh Executable file
View File

@ -0,0 +1,91 @@
#!/bin/bash
SCRIPT=$(readlink -f "$0")
INSTALLPATH=$(dirname "${SCRIPT}")
TOPDIR=$(dirname "${INSTALLPATH}")
default_seahub_db=${TOPDIR}/seahub.db
prev_version=0.9.3
current_version=0.9.4
echo
echo "-------------------------------------------------------------"
echo "This script would upgrade your seafile server from ${prev_version} to ${current_version}"
echo "Press [ENTER] to contiune"
echo "-------------------------------------------------------------"
echo
read dummy
# check for python-imaging and python-simplejson
if ! python -c "import simplejson" 2>/dev/null 1>&2; then
echo "python-simplejson needs to be installed."
echo "On Debian/Ubntu: apt-get install python-simplejson"
echo "On CentOS/RHEL: yum install python-simplejson"
echo
exit 1
fi
if ! python -c "import PIL" 2>/dev/null 1>&2; then
echo "python-imaging needs to be installed."
echo "On Debian/Ubntu: apt-get install python-imaging"
echo "On CentOS/RHEL: yum install python-imaging"
echo
exit 1
fi
# test whether seafile server has been stopped.
if pgrep seaf-server 2>/dev/null 1>&2 ; then
echo
echo "seafile server is still running !"
echo "stop it using scripts before upgrade."
echo
exit 1
elif pgrep -f "manage.py run_gunicorn" 2>/dev/null 1>&2 ; then
echo
echo "seahub server is still running !"
echo "stop it before upgrade."
echo
exit 1
fi
# run django syncdb command
echo "------------------------------"
echo "updating seahub database ... "
export PYTHONPATH=${INSTALLPATH}/seafile/lib/python2.7/site-packages:${INSTALLPATH}/seahub/thirdpart:${PYTHONPATH}
manage_py=${INSTALLPATH}/seahub/manage.py
pushd "${INSTALLPATH}/seahub" 2>/dev/null 1>&2
python manage.py syncdb 2>/dev/null 1>&2 && echo "Done." || echo "Failed."
popd 2>/dev/null 1>&2
echo "------------------------------"
media_dir=${INSTALLPATH}/seahub/media
orig_avatar_dir=${INSTALLPATH}/seahub/media/avatars
dest_avatar_dir=${TOPDIR}/seahub-data/avatars
# move "media/avatars" directory outside
if [[ ! -d ${dest_avatar_dir} ]]; then
mkdir -p "${TOPDIR}/seahub-data"
mv "${orig_avatar_dir}" "${dest_avatar_dir}" 2>/dev/null 1>&2
ln -s ../../../seahub-data/avatars ${media_dir}
elif [[ ! -L ${orig_avatar_dir}} ]]; then
mv ${orig_avatar_dir}/* "${dest_avatar_dir}" 2>/dev/null 1>&2
rm -rf "${orig_avatar_dir}"
ln -s ../../../seahub-data/avatars ${media_dir}
fi
seahub_settings=${TOPDIR}/seahub_settings.py
function gen_seahub_secret_key () {
seahub_secret_keygen=${INSTALLPATH}/seahub/tools/secret_key_generator.py
echo -n -e "\nSECRET_KEY = " >> "${seahub_settings}"
key=$(python "${seahub_secret_keygen}")
echo "\"${key}\"" >> "${seahub_settings}"
}
if ! grep SECRET_KEY "${seahub_settings}" 2>/dev/null 1>&2 ; then
gen_seahub_secret_key;
fi