mirror of
https://github.com/haiwen/seafile.git
synced 2025-01-08 11:57:44 +08:00
Add monitor, httpserver, controller, tests and tools.
This commit is contained in:
parent
1671133d5e
commit
449772e147
27
.gitignore
vendored
27
.gitignore
vendored
@ -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/
|
||||
|
16
Makefile.am
16
Makefile.am
@ -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
|
||||
|
41
configure.ac
41
configure.ac
@ -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
22
controller/Makefile.am
Normal 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@
|
657
controller/seafile-controller.c
Normal file
657
controller/seafile-controller.c
Normal 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;
|
||||
}
|
56
controller/seafile-controller.h
Normal file
56
controller/seafile-controller.h
Normal 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
53
httpserver/Makefile.am
Normal 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
7
httpserver/README
Normal 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
|
6
httpserver/htmls/content-type.txt
Normal file
6
httpserver/htmls/content-type.txt
Normal file
@ -0,0 +1,6 @@
|
||||
txt text/plain
|
||||
html text/html
|
||||
doc application/word
|
||||
docx application/word
|
||||
mp3 audio/mp3
|
||||
pdf application/pdf
|
8
httpserver/htmls/index.html
Normal file
8
httpserver/htmls/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Seafile Httpserver</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Welcome to seafile httpserver</p>
|
||||
</body>
|
||||
</html>
|
24
httpserver/htmls/upload.html
Normal file
24
httpserver/htmls/upload.html
Normal 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
639
httpserver/httpserver.c
Normal 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
9
httpserver/httpserver.h
Normal 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
823
httpserver/repo-mgr.c
Normal 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
162
httpserver/repo-mgr.h
Normal 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
|
105
httpserver/seafile-session.c
Normal file
105
httpserver/seafile-session.c
Normal 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;
|
||||
}
|
47
httpserver/seafile-session.h
Normal file
47
httpserver/seafile-session.h
Normal 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
55
monitor/Makefile.am
Normal 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
19
monitor/monitor-rpc.c
Normal 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;
|
||||
}
|
130
monitor/processors/heartbeat-proc.c
Normal file
130
monitor/processors/heartbeat-proc.c
Normal 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);
|
||||
}
|
||||
}
|
30
monitor/processors/heartbeat-proc.h
Normal file
30
monitor/processors/heartbeat-proc.h
Normal 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
|
||||
|
229
monitor/processors/repostat-proc.c
Normal file
229
monitor/processors/repostat-proc.c
Normal 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);
|
||||
}
|
30
monitor/processors/repostat-proc.h
Normal file
30
monitor/processors/repostat-proc.h
Normal 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
823
monitor/repo-mgr.c
Normal 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
162
monitor/repo-mgr.h
Normal 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
194
monitor/scheduler.c
Normal 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
23
monitor/scheduler.h
Normal 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
230
monitor/seaf-mon.c
Normal 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
158
monitor/seafile-session.c
Normal 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
60
monitor/seafile-session.h
Normal 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
24
tests/Makefile.am
Normal 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
4
tests/README
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
Test Cases
|
||||
==========
|
||||
|
66
tests/basic/README
Normal file
66
tests/basic/README
Normal 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
11
tests/basic/clean.sh
Executable 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/*
|
13
tests/basic/conf1/ccnet.conf
Normal file
13
tests/basic/conf1/ccnet.conf
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
[General]
|
||||
USER_NAME=plt
|
||||
ID=eed994152b231c673eeb5f586c06cd20cf9d10e8
|
||||
NAME=plt
|
||||
|
||||
[Network]
|
||||
|
||||
DEFAULT_RELAY=8e4b13b49ca79f35732d9f44a0804940d985627c
|
||||
PORT=10001
|
||||
|
||||
[Client]
|
||||
PORT=10000
|
27
tests/basic/conf1/mykey.peer
Normal file
27
tests/basic/conf1/mykey.peer
Normal 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-----
|
10
tests/basic/conf2/ccnet.conf
Normal file
10
tests/basic/conf2/ccnet.conf
Normal file
@ -0,0 +1,10 @@
|
||||
[General]
|
||||
USER_NAME = server
|
||||
ID = 8e4b13b49ca79f35732d9f44a0804940d985627c
|
||||
NAME = server
|
||||
|
||||
[Network]
|
||||
PORT = 10002
|
||||
|
||||
[Client]
|
||||
PORT = 9999
|
27
tests/basic/conf2/mykey.peer
Normal file
27
tests/basic/conf2/mykey.peer
Normal 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-----
|
10
tests/basic/conf3/ccnet.conf
Normal file
10
tests/basic/conf3/ccnet.conf
Normal file
@ -0,0 +1,10 @@
|
||||
[General]
|
||||
USER_NAME = freeplant
|
||||
ID = e19d1070b6d9ae7bf453d124ff4e775076553906
|
||||
NAME = freeplant
|
||||
|
||||
[Network]
|
||||
PORT = 10003
|
||||
|
||||
[Client]
|
||||
PORT = 9998
|
27
tests/basic/conf3/mykey.peer
Normal file
27
tests/basic/conf3/mykey.peer
Normal 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-----
|
10
tests/basic/conf4/ccnet.conf
Normal file
10
tests/basic/conf4/ccnet.conf
Normal file
@ -0,0 +1,10 @@
|
||||
[General]
|
||||
USER_NAME = server2
|
||||
ID = 9b7adea2e0a954196734af25837751bb833336e6
|
||||
NAME = server2
|
||||
|
||||
[Network]
|
||||
PORT = 10004
|
||||
|
||||
[Client]
|
||||
PORT = 9997
|
27
tests/basic/conf4/mykey.peer
Normal file
27
tests/basic/conf4/mykey.peer
Normal 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
27
tests/basic/lan.sh
Executable 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
53
tests/basic/seafile.sh
Executable 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
45
tests/basic/setup.sh
Executable 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
14
tests/basic/teardown.sh
Executable 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/*
|
48
tests/basic/test-branch-name-whitespace.sh
Executable file
48
tests/basic/test-branch-name-whitespace.sh
Executable 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
44
tests/basic/test-branch-show.sh
Executable 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
63
tests/basic/test-diff-rename.sh
Executable 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
65
tests/basic/test-diff.sh
Executable 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
67
tests/basic/test-diff2.sh
Executable 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
62
tests/basic/test-diff3.sh
Executable 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
|
93
tests/basic/test-duplicate-repo-name.sh
Executable file
93
tests/basic/test-duplicate-repo-name.sh
Executable 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
|
20
tests/basic/test-encrypt.README
Normal file
20
tests/basic/test-encrypt.README
Normal 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
72
tests/basic/test-encrypt.sh
Executable 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
|
82
tests/basic/test-merge-conflict-status.sh
Executable file
82
tests/basic/test-merge-conflict-status.sh
Executable 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}
|
45
tests/basic/test-new-checkout.sh
Executable file
45
tests/basic/test-new-checkout.sh
Executable 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
|
49
tests/basic/test-refactor-worktree.sh
Executable file
49
tests/basic/test-refactor-worktree.sh
Executable 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
|
63
tests/basic/test-restful-put-file.sh
Executable file
63
tests/basic/test-restful-put-file.sh
Executable 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
56
tests/basic/test-rm.sh
Executable 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
59
tests/basic/test-share.sh
Executable 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}
|
56
tests/basic/test-status-rename.sh
Executable file
56
tests/basic/test-status-rename.sh
Executable 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}
|
47
tests/basic/test-status-timestamp.sh
Executable file
47
tests/basic/test-status-timestamp.sh
Executable 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
63
tests/basic/test-status.sh
Executable 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
13
tests/common-conf.sh.in
Normal 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
78
tests/common.py
Normal 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
125
tests/test-cdc.c
Normal 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
105
tests/test-crypt.c
Normal 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
48
tests/test-index.c
Normal 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
10
tests/test-rpc.py
Normal 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
98
tests/test-seafile-fmt.c
Normal 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
122
tests/test-share.py
Executable 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
120
tests/test-transfer.py
Executable 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
11
tools/Makefile.am
Normal 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
12
tools/README
Normal 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
21
tools/conv-obj-store.py
Executable 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
44
tools/cp-setup-files.sh
Executable 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
126
tools/create-seahub-admin.sh
Executable 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
35
tools/pack-server.sh
Executable 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
219
tools/seaf-server-init.c
Normal 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
119
tools/seafile.sh
Executable 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
12
tools/seahub.conf
Normal 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
124
tools/seahub.sh
Executable 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
492
tools/setup-seafile.sh
Executable 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
91
tools/upgrade-server.sh
Executable 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
|
||||
|
Loading…
Reference in New Issue
Block a user