Cmdline: move cmdline to dbus server (MB#5043)

Implement cmdline with support of dbus server. To enable cmdline
with dbus server, use the option '--use-daemon yes/no' in case that
you enable dbus service when configuration.

In a typical scenario, a new session is created for the purpose of
execution of arguments. It is scheduled with other sessions but with
a highest priority. Once it becomes active, command line call
'Session.Execute', a newly added method to execute command line
arguments.

The config name of a session should be known for dbus clients like
command line. A new property 'configName' is added in the properties
when calling 'Session.GetConfig'.

CTRL-C handling are processed once executing a real sync to dbus
server. It is mapped to invoke 'Session.Suspend' and 'Session.Abort'.

The meaning of '--enable-dbus-service' is expanded accordingly.

'--status' without server means printing all running session in the
dbus server.
'--monitor' could accept an optional config name. If one is given,
only attach to a session of that config, otherwise print an error.
If none is given, pick the first.
This commit is contained in:
Zhu, Yongsheng 2010-03-15 09:33:43 +08:00 committed by Patrick Ohly
parent c6ee383995
commit 3fc7e529ea
9 changed files with 1619 additions and 42 deletions

View file

@ -348,7 +348,8 @@ AM_CONDITIONAL([COND_CORE], [test "$enable_core" = "yes"])
AC_ARG_ENABLE(dbus-service,
AS_HELP_STRING([--enable-dbus-service],
[enables building the dbus service executable and the wrapper library for it]),
[enables building the dbus service executable and all related features
(the DBus wrapper library, command line usage of server, etc).]),
enable_dbus_service="$enableval",
[if test $enable_gui == "no"; then
enable_dbus_service="no"
@ -380,6 +381,7 @@ if test $enable_dbus_service == "yes"; then
if test $HAVE_LIBNOTIFY == "yes"; then
AC_DEFINE(HAS_NOTIFY, 1, [define if libnotify could be used in dbus service])
fi
AC_DEFINE(DBUS_SERVICE, 1, [define if dbus service is enabled])
fi
AC_SUBST(DBUS_CFLAGS)
AC_SUBST(DBUS_LIBS)

View file

@ -122,9 +122,9 @@ endif
# SYNCEVOLUTION_LDADD will be replaced with libsyncebook.la/libsyncecal.la/libsyncsqlite.la
# if linking statically against them, empty otherwise;
# either way this does not lead to a dependency on those libs - done explicitly
syncevolution_LDADD = $(CORE_LDADD) $(KEYRING_LIBS)
syncevolution_LDADD = gdbus/libgdbus.la $(CORE_LDADD) $(KEYRING_LIBS)
syncevolution_LDFLAGS = $(CORE_LD_FLAGS)
syncevolution_CXXFLAGS = $(SYNCEVOLUTION_CXXFLAGS) $(CORE_CXXFLAGS) $(KEYRING_CFLAGS)
syncevolution_CXXFLAGS = $(SYNCEVOLUTION_CXXFLAGS) $(CORE_CXXFLAGS) $(KEYRING_CFLAGS) -I$(srcdir)/gdbus
syncevolution_DEPENDENCIES = $(EXTRA_LTLIBRARIES) $(CORE_DEP) # $(SYNTHESIS_DEP)
# rule which is only relevant when compiling Synthesis in subdirectory

View file

@ -4027,9 +4027,9 @@ public:
};
/*
* A DBus Client Call object handling 0 argument and 1 return value.
* A DBus Client Call object handling zero or more parameter and
* zero return value.
*/
template <class R>
class DBusClientCall0
{
const std::string m_destination;
@ -4038,6 +4038,114 @@ class DBusClientCall0
const std::string m_method;
const DBusConnectionPtr m_conn;
/** called by libdbus on error or completion of call */
static void dbusCallback (DBusPendingCall *call, void *user_data)
{
DBusMessagePtr reply = dbus_pending_call_steal_reply (call);
const char* errname = dbus_message_get_error_name (reply.get());
std::string error;
if (errname) {
error = errname;
}
//unmarshal the return results and call user callback
(*static_cast <Callback_t *>(user_data))(error);
}
/**
* called by libdbus to free the user_data pointer set in
* dbus_pending_call_set_notify()
*/
static void callDataUnref(void *user_data) {
delete static_cast <Callback_t *>(user_data);
}
public:
/**
* called when result of call is available or an error occurred (non-empty string)
*/
typedef boost::function<void (const std::string &)> Callback_t;
DBusClientCall0 (const DBusCallObject &object)
:m_destination (object.getDestination()),
m_path (object.getPath()),
m_interface (object.getInterface()),
m_method (object.getMethod()),
m_conn (object.getConnection())
{
}
DBusClientCall0 (const DBusRemoteObject &object, const std::string &method)
:m_destination (object.getDestination()),
m_path (object.getPath()),
m_interface (object.getInterface()),
m_method (method),
m_conn (object.getConnection())
{
}
void operator () (const Callback_t &callback)
{
DBusPendingCall *call;
DBusMessagePtr msg(dbus_message_new_method_call(
m_destination.c_str(),
m_path.c_str(),
m_interface.c_str(),
m_method.c_str()));
if (!msg) {
throw std::runtime_error("dbus_message_new_method_call() failed");
}
//parameter marshaling (none)
if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
throw std::runtime_error("dbus_connection_send failed");
}
DBusPendingCallPtr mCall (call);
Callback_t *data = new Callback_t(callback);
dbus_pending_call_set_notify(mCall.get(),
dbusCallback,
data,
callDataUnref);
}
template <class A1>
void operator () (const A1 &a1, const Callback_t &callback)
{
DBusPendingCall *call;
DBusMessagePtr msg(dbus_message_new_method_call(
m_destination.c_str(),
m_path.c_str(),
m_interface.c_str(),
m_method.c_str()));
if (!msg) {
throw std::runtime_error("dbus_message_new_method_call() failed");
}
append_retvals(msg, a1);
//parameter marshaling (none)
if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
throw std::runtime_error("dbus_connection_send failed");
}
DBusPendingCallPtr mCall (call);
Callback_t *data = new Callback_t(callback);
dbus_pending_call_set_notify(mCall.get(),
dbusCallback,
data,
callDataUnref);
}
};
/** 1 return value and 0 or more parameters */
template <class R>
class DBusClientCall1
{
const std::string m_destination;
const std::string m_path;
const std::string m_interface;
const std::string m_method;
const DBusConnectionPtr m_conn;
/** called by libdbus on error or completion of call */
static void dbusCallback (DBusPendingCall *call, void *user_data)
{
@ -4067,11 +4175,11 @@ class DBusClientCall0
public:
/**
* called when result of call is available (R) or an error occurred (non-empty string)
* called when the call is returned or an error occurred (non-empty string)
*/
typedef boost::function<void (const R &, const std::string &)> Callback_t;
DBusClientCall0 (const DBusCallObject &object)
DBusClientCall1 (const DBusCallObject &object)
:m_destination (object.getDestination()),
m_path (object.getPath()),
m_interface (object.getInterface()),
@ -4080,7 +4188,7 @@ public:
{
}
DBusClientCall0 (const DBusRemoteObject &object, const std::string &method)
DBusClientCall1 (const DBusRemoteObject &object, const std::string &method)
:m_destination (object.getDestination()),
m_path (object.getPath()),
m_interface (object.getInterface()),
@ -4113,6 +4221,207 @@ public:
data,
callDataUnref);
}
template <class A1>
void operator () (const A1 &a1, const Callback_t &callback)
{
DBusPendingCall *call;
DBusMessagePtr msg(dbus_message_new_method_call(
m_destination.c_str(),
m_path.c_str(),
m_interface.c_str(),
m_method.c_str()));
if (!msg) {
throw std::runtime_error("dbus_message_new_method_call() failed");
}
append_retvals(msg, a1);
//parameter marshaling (none)
if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
throw std::runtime_error("dbus_connection_send failed");
}
DBusPendingCallPtr mCall (call);
Callback_t *data = new Callback_t(callback);
dbus_pending_call_set_notify(mCall.get(),
dbusCallback,
data,
callDataUnref);
}
template <class A1, class A2>
void operator () (const A1 &a1, const A2 &a2, const Callback_t &callback)
{
DBusPendingCall *call;
DBusMessagePtr msg(dbus_message_new_method_call(
m_destination.c_str(),
m_path.c_str(),
m_interface.c_str(),
m_method.c_str()));
if (!msg) {
throw std::runtime_error("dbus_message_new_method_call() failed");
}
append_retvals(msg, a1);
append_retvals(msg, a2);
//parameter marshaling (none)
if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
throw std::runtime_error("dbus_connection_send failed");
}
DBusPendingCallPtr mCall (call);
Callback_t *data = new Callback_t(callback);
dbus_pending_call_set_notify(mCall.get(),
dbusCallback,
data,
callDataUnref);
}
};
/** 3 return value and 0 or more parameters */
template <class R1, class R2, class R3>
class DBusClientCall3
{
const std::string m_destination;
const std::string m_path;
const std::string m_interface;
const std::string m_method;
const DBusConnectionPtr m_conn;
/** called by libdbus on error or completion of call */
static void dbusCallback (DBusPendingCall *call, void *user_data)
{
DBusMessagePtr reply = dbus_pending_call_steal_reply (call);
const char* errname = dbus_message_get_error_name (reply.get());
std::string error;
typename dbus_traits<R1>::host_type r1;
typename dbus_traits<R2>::host_type r2;
typename dbus_traits<R3>::host_type r3;
if (!errname) {
DBusMessageIter iter;
dbus_message_iter_init(reply.get(), &iter);
//why need connection?
dbus_traits<R1>::get(NULL, reply.get(), iter, r1);
dbus_traits<R2>::get(NULL, reply.get(), iter, r2);
dbus_traits<R3>::get(NULL, reply.get(), iter, r3);
} else {
error = errname;
}
//unmarshal the return results and call user callback
(*static_cast <Callback_t *>(user_data))(r1, r2, r3, error);
}
/**
* called by libdbus to free the user_data pointer set in
* dbus_pending_call_set_notify()
*/
static void callDataUnref(void *user_data) {
delete static_cast <Callback_t *>(user_data);
}
public:
/**
* called when the call is returned or an error occurred (non-empty string)
*/
typedef boost::function<void (const R1 &, const R2 &, const R3 &, const std::string &)> Callback_t;
DBusClientCall3 (const DBusCallObject &object)
:m_destination (object.getDestination()),
m_path (object.getPath()),
m_interface (object.getInterface()),
m_method (object.getMethod()),
m_conn (object.getConnection())
{
}
DBusClientCall3 (const DBusRemoteObject &object, const std::string &method)
:m_destination (object.getDestination()),
m_path (object.getPath()),
m_interface (object.getInterface()),
m_method (method),
m_conn (object.getConnection())
{
}
void operator () (const Callback_t &callback)
{
DBusPendingCall *call;
DBusMessagePtr msg(dbus_message_new_method_call(
m_destination.c_str(),
m_path.c_str(),
m_interface.c_str(),
m_method.c_str()));
if (!msg) {
throw std::runtime_error("dbus_message_new_method_call() failed");
}
//parameter marshaling (none)
if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
throw std::runtime_error("dbus_connection_send failed");
}
DBusPendingCallPtr mCall (call);
Callback_t *data = new Callback_t(callback);
dbus_pending_call_set_notify(mCall.get(),
dbusCallback,
data,
callDataUnref);
}
template <class A1>
void operator () (const A1 &a1, const Callback_t &callback)
{
DBusPendingCall *call;
DBusMessagePtr msg(dbus_message_new_method_call(
m_destination.c_str(),
m_path.c_str(),
m_interface.c_str(),
m_method.c_str()));
if (!msg) {
throw std::runtime_error("dbus_message_new_method_call() failed");
}
append_retvals(msg, a1);
//parameter marshaling (none)
if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
throw std::runtime_error("dbus_connection_send failed");
}
DBusPendingCallPtr mCall (call);
Callback_t *data = new Callback_t(callback);
dbus_pending_call_set_notify(mCall.get(),
dbusCallback,
data,
callDataUnref);
}
template <class A1, class A2>
void operator () (const A1 &a1, const A2 &a2, const Callback_t &callback)
{
DBusPendingCall *call;
DBusMessagePtr msg(dbus_message_new_method_call(
m_destination.c_str(),
m_path.c_str(),
m_interface.c_str(),
m_method.c_str()));
if (!msg) {
throw std::runtime_error("dbus_message_new_method_call() failed");
}
append_retvals(msg, a1);
append_retvals(msg, a2);
//parameter marshaling (none)
if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
throw std::runtime_error("dbus_connection_send failed");
}
DBusPendingCallPtr mCall (call);
Callback_t *data = new Callback_t(callback);
dbus_pending_call_set_notify(mCall.get(),
dbusCallback,
data,
callDataUnref);
}
};
class SignalWatch
@ -4149,6 +4458,52 @@ class SignalWatch
virtual ~SignalWatch() {}
};
class SignalWatch0 : public SignalWatch
{
typedef boost::function<void (void)> Callback_t;
guint m_tag;
Callback_t *m_callback;
public:
SignalWatch0(const DBusRemoteObject &object,
const std::string &signal)
: SignalWatch(object, signal), m_tag(0), m_callback(0)
{
}
~SignalWatch0()
{
if(m_tag) {
g_dbus_remove_watch(m_object.getConnection(), m_tag);
}
if(m_callback) {
delete m_callback;
}
}
static gboolean internalCallback(DBusConnection *conn, DBusMessage *msg, void *data)
{
if(isMatched(msg, data) == FALSE) {
return TRUE;
}
SignalWatch0 *watch = static_cast<SignalWatch0* >(data);
(*watch->m_callback)();
return TRUE;
}
void operator () (const Callback_t &callback)
{
m_callback = new Callback_t(callback);
std::string rule = makeSignalRule();
m_tag = g_dbus_add_signal_watch(m_object.getConnection(),
rule.c_str(),
internalCallback,
this,
NULL);
}
};
template <typename A1>
class SignalWatch1 : public SignalWatch
{
@ -4257,4 +4612,244 @@ class SignalWatch2 : public SignalWatch
}
};
template <typename A1, typename A2, typename A3>
class SignalWatch3 : public SignalWatch
{
typedef boost::function<void (const A1 &, const A2 &, const A3 &)> Callback_t;
guint m_tag;
Callback_t *m_callback;
public:
SignalWatch3(const DBusRemoteObject &object,
const std::string &signal)
: SignalWatch(object, signal), m_tag(0), m_callback(0)
{
}
~SignalWatch3()
{
if(m_tag) {
g_dbus_remove_watch(m_object.getConnection(), m_tag);
}
if(m_callback) {
delete m_callback;
}
}
static gboolean internalCallback(DBusConnection *conn, DBusMessage *msg, void *data)
{
if(isMatched(msg, data) == FALSE) {
return TRUE;
}
SignalWatch3<A1, A2, A3> *watch = static_cast<SignalWatch3<A1, A2, A3> *>(data);
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
typename dbus_traits<A3>::host_type a3;
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
dbus_traits<A1>::get(conn, msg, iter, a1);
dbus_traits<A2>::get(conn, msg, iter, a2);
dbus_traits<A3>::get(conn, msg, iter, a3);
(*watch->m_callback)(a1, a2, a3);
return TRUE;
}
void operator () (const Callback_t &callback)
{
m_callback = new Callback_t(callback);
std::string rule = makeSignalRule();
m_tag = g_dbus_add_signal_watch(m_object.getConnection(),
rule.c_str(),
internalCallback,
this,
NULL);
}
};
template <typename A1, typename A2, typename A3, typename A4>
class SignalWatch4 : public SignalWatch
{
typedef boost::function<void (const A1 &, const A2 &, const A3 &, const A4 &)> Callback_t;
guint m_tag;
Callback_t *m_callback;
public:
SignalWatch4(const DBusRemoteObject &object,
const std::string &signal)
: SignalWatch(object, signal), m_tag(0), m_callback(0)
{
}
~SignalWatch4()
{
if(m_tag) {
g_dbus_remove_watch(m_object.getConnection(), m_tag);
}
if(m_callback) {
delete m_callback;
}
}
static gboolean internalCallback(DBusConnection *conn, DBusMessage *msg, void *data)
{
if(isMatched(msg, data) == FALSE) {
return TRUE;
}
SignalWatch4<A1, A2, A3, A4> *watch = static_cast<SignalWatch4<A1, A2, A3, A4> *>(data);
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
typename dbus_traits<A3>::host_type a3;
typename dbus_traits<A4>::host_type a4;
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
dbus_traits<A1>::get(conn, msg, iter, a1);
dbus_traits<A2>::get(conn, msg, iter, a2);
dbus_traits<A3>::get(conn, msg, iter, a3);
dbus_traits<A4>::get(conn, msg, iter, a4);
(*watch->m_callback)(a1, a2, a3, a4);
return TRUE;
}
void operator () (const Callback_t &callback)
{
m_callback = new Callback_t(callback);
std::string rule = makeSignalRule();
m_tag = g_dbus_add_signal_watch(m_object.getConnection(),
rule.c_str(),
internalCallback,
this,
NULL);
}
};
template <typename A1, typename A2, typename A3, typename A4, typename A5>
class SignalWatch5 : public SignalWatch
{
typedef boost::function<void (const A1 &, const A2 &, const A3 &, const A4 &, const A5 &)> Callback_t;
guint m_tag;
Callback_t *m_callback;
public:
SignalWatch5(const DBusRemoteObject &object,
const std::string &signal)
: SignalWatch(object, signal), m_tag(0), m_callback(0)
{
}
~SignalWatch5()
{
if(m_tag) {
g_dbus_remove_watch(m_object.getConnection(), m_tag);
}
if(m_callback) {
delete m_callback;
}
}
static gboolean internalCallback(DBusConnection *conn, DBusMessage *msg, void *data)
{
if(isMatched(msg, data) == FALSE) {
return TRUE;
}
SignalWatch5<A1, A2, A3, A4, A5> *watch = static_cast<SignalWatch5<A1, A2, A3, A4, A5> *>(data);
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
typename dbus_traits<A3>::host_type a3;
typename dbus_traits<A4>::host_type a4;
typename dbus_traits<A5>::host_type a5;
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
dbus_traits<A1>::get(conn, msg, iter, a1);
dbus_traits<A2>::get(conn, msg, iter, a2);
dbus_traits<A3>::get(conn, msg, iter, a3);
dbus_traits<A4>::get(conn, msg, iter, a4);
dbus_traits<A5>::get(conn, msg, iter, a5);
(*watch->m_callback)(a1, a2, a3, a4, a5);
return TRUE;
}
void operator () (const Callback_t &callback)
{
m_callback = new Callback_t(callback);
std::string rule = makeSignalRule();
m_tag = g_dbus_add_signal_watch(m_object.getConnection(),
rule.c_str(),
internalCallback,
this,
NULL);
}
};
template <typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
class SignalWatch6 : public SignalWatch
{
typedef boost::function<void (const A1 &, const A2 &, const A3 &, const A4 &, const A5 &, const A6 &)> Callback_t;
guint m_tag;
Callback_t *m_callback;
public:
SignalWatch6(const DBusRemoteObject &object,
const std::string &signal)
: SignalWatch(object, signal), m_tag(0), m_callback(0)
{
}
~SignalWatch6()
{
if(m_tag) {
g_dbus_remove_watch(m_object.getConnection(), m_tag);
}
if(m_callback) {
delete m_callback;
}
}
static gboolean internalCallback(DBusConnection *conn, DBusMessage *msg, void *data)
{
if(isMatched(msg, data) == FALSE) {
return TRUE;
}
SignalWatch6<A1, A2, A3, A4, A5, A6> *watch = static_cast<SignalWatch6<A1, A2, A3, A4, A5, A6> *>(data);
typename dbus_traits<A1>::host_type a1;
typename dbus_traits<A2>::host_type a2;
typename dbus_traits<A3>::host_type a3;
typename dbus_traits<A4>::host_type a4;
typename dbus_traits<A5>::host_type a5;
typename dbus_traits<A6>::host_type a6;
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
dbus_traits<A1>::get(conn, msg, iter, a1);
dbus_traits<A2>::get(conn, msg, iter, a2);
dbus_traits<A3>::get(conn, msg, iter, a3);
dbus_traits<A4>::get(conn, msg, iter, a4);
dbus_traits<A5>::get(conn, msg, iter, a5);
dbus_traits<A6>::get(conn, msg, iter, a6);
(*watch->m_callback)(a1, a2, a3, a4, a5, a6);
return TRUE;
}
void operator () (const Callback_t &callback)
{
m_callback = new Callback_t(callback);
std::string rule = makeSignalRule();
m_tag = g_dbus_add_signal_watch(m_object.getConnection(),
rule.c_str(),
internalCallback,
this,
NULL);
}
};
#endif // INCL_GDBUS_CXX_BRIDGE

View file

@ -31,6 +31,7 @@
#include <syncevo/SyncSource.h>
#include <syncevo/SyncML.h>
#include <syncevo/FileConfigNode.h>
#include <syncevo/Cmdline.h>
#include <synthesis/san.h>
@ -1716,7 +1717,9 @@ class Session : public DBusObjectHelper,
enum RunOperation {
OP_SYNC = 0,
OP_RESTORE
OP_RESTORE = 1,
OP_CMDLINE = 2,
OP_NULL
};
static string runOpToString(RunOperation op);
@ -1726,6 +1729,9 @@ class Session : public DBusObjectHelper,
/** listener to listen to changes of sync */
SessionListener *m_listener;
/** Cmdline to execute command line args */
boost::shared_ptr<CmdlineWrapper> m_cmdline;
/** Session.Detach() */
void detach(const Caller_t &caller);
@ -1743,6 +1749,9 @@ class Session : public DBusObjectHelper,
/** Session.checkPresence() */
void checkPresence (string &status);
/** Session.Execute() */
void execute(const vector<string> &args);
/**
* Must be called each time that properties changing the
* overall status are changed. Ensures that the corresponding
@ -1776,6 +1785,7 @@ public:
~Session();
enum {
PRI_CMDLINE = -10,
PRI_DEFAULT = 0,
PRI_CONNECTION = 10,
PRI_AUTOSYNC = 20
@ -1816,7 +1826,7 @@ public:
/**
* TRUE if the session is ready to take over control
*/
bool readyToRun() { return (m_syncStatus != SYNC_DONE) && m_sync; }
bool readyToRun() { return (m_syncStatus != SYNC_DONE) && (m_runOperation != OP_NULL); }
/**
* transfer control to the session for the duration of the sync,
@ -1927,8 +1937,28 @@ class CmdlineWrapper
/** stream for command line out and err arguments */
std::ostream m_cmdlineOutStream;
/**
* implement factory method to create DBusSync instances
* This can check 'abort' and 'suspend' command from clients.
*/
class DBusCmdline : public Cmdline {
Session &m_session;
public:
DBusCmdline(Session &session,
const vector<string> &args,
ostream &out,
ostream &err)
:Cmdline(args, out, err), m_session(session)
{}
SyncContext* createSyncClient() {
return new DBusSync(m_server, m_session);
}
};
/** instance to run command line arguments */
Cmdline m_cmdline;
DBusCmdline m_cmdline;
public:
/**
* constructor to create cmdline instance.
@ -1938,7 +1968,7 @@ public:
*/
CmdlineWrapper(Session &session, const vector<string> &args)
: m_cmdlineOutStream(&m_outStreamBuf),
m_cmdline(args, m_cmdlineOutStream, m_cmdlineOutStream)
m_cmdline(session, args, m_cmdlineOutStream, m_cmdlineOutStream)
{}
void parse() { m_cmdline.parse(); }
@ -2407,6 +2437,9 @@ void ReadOperations::getConfig(bool getTemplate,
}
}
//insert 'configName'
localConfigs.insert(pair<string, string>("configName", m_configName));
config.insert(pair<string,map<string, string> >("", localConfigs));
/* get configurations from sources */
@ -2746,8 +2779,9 @@ void Session::setConfig(bool update, bool temporary,
if (!m_active) {
SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
}
if (m_sync) {
SE_THROW_EXCEPTION(InvalidCall, "sync started, cannot change configuration at this time");
if (m_runOperation != OP_NULL) {
string msg = StringPrintf("%s started, cannot change configuration at this time", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
}
if (!update && temporary) {
throw std::runtime_error("Clearing existing configuration and temporary configuration changes which only affects the duration of the session are mutually exclusive");
@ -2828,8 +2862,11 @@ void Session::sync(const std::string &mode, const SourceModes_t &source_modes)
if (!m_active) {
SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
}
if (m_sync) {
string msg = StringPrintf("%s started, cannot start(again)", runOpToString(m_runOperation).c_str());
if (m_runOperation == OP_SYNC) {
string msg = StringPrintf("%s started, cannot start again", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
} else if (m_runOperation != OP_NULL) {
string msg = StringPrintf("%s started, cannot start sync", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
}
@ -2888,7 +2925,7 @@ void Session::sync(const std::string &mode, const SourceModes_t &source_modes)
void Session::abort()
{
if (!m_sync) {
if (m_runOperation != OP_SYNC && m_runOperation != OP_CMDLINE) {
SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot abort at this time");
}
m_syncStatus = SYNC_ABORT;
@ -2900,7 +2937,7 @@ void Session::abort()
void Session::suspend()
{
if (!m_sync) {
if (m_runOperation != OP_SYNC && m_runOperation != OP_CMDLINE) {
SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot suspend at this time");
}
m_syncStatus = SYNC_SUSPEND;
@ -3007,7 +3044,7 @@ Session::Session(DBusServer &server,
m_restoreBefore(true),
m_restoreSrcTotal(0),
m_restoreSrcEnd(0),
m_runOperation(OP_SYNC),
m_runOperation(OP_NULL),
m_listener(NULL),
emitStatus(*this, "StatusChanged"),
emitProgress(*this, "ProgressChanged")
@ -3026,6 +3063,7 @@ Session::Session(DBusServer &server,
add(this, &Session::getProgress, "GetProgress");
add(this, &Session::restore, "Restore");
add(this, &Session::checkPresence, "checkPresence");
add(this, &Session::execute, "Execute");
add(emitStatus);
add(emitProgress);
}
@ -3193,7 +3231,7 @@ void Session::sourceProgress(sysync::TProgressEventEnum type,
void Session::run()
{
if (m_sync) {
if (m_runOperation != OP_NULL) {
try {
m_syncStatus = SYNC_RUNNING;
fireStatus(true);
@ -3224,6 +3262,8 @@ void Session::run()
m_sync->restore(m_restoreDir,
m_restoreBefore ? SyncContext::DATABASE_BEFORE_SYNC : SyncContext::DATABASE_AFTER_SYNC);
break;
case OP_CMDLINE:
m_cmdline->run();
default:
break;
};
@ -3238,7 +3278,7 @@ void Session::run()
m_syncStatus = SYNC_DONE;
m_stepIsWaiting = false;
fireStatus(true);
}
}
}
bool Session::setFilters(SyncConfig &config)
@ -3266,10 +3306,13 @@ void Session::restore(const string &dir, bool before, const std::vector<std::str
if (!m_active) {
SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
}
if (m_sync) {
if (m_runOperation == OP_RESTORE) {
string msg = StringPrintf("restore started, cannot restore again");
SE_THROW_EXCEPTION(InvalidCall, msg);
} else if (m_runOperation != OP_NULL) {
// actually this never happen currently, for during the real restore process,
// it never poll the sources in default main context
string msg = StringPrintf("%s started, cannot restore(again)", runOpToString(m_runOperation).c_str());
string msg = StringPrintf("%s started, cannot restore", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
}
@ -3308,11 +3351,32 @@ string Session::runOpToString(RunOperation op)
return "sync";
case OP_RESTORE:
return "restore";
case OP_CMDLINE:
return "cmdline";
default:
return "";
};
}
void Session::execute(const vector<string> &args)
{
if (!m_active) {
SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
}
if (m_runOperation == OP_CMDLINE) {
SE_THROW_EXCEPTION(InvalidCall, "cmdline started, cannot start again");
} else if (m_runOperation != OP_NULL) {
string msg = StringPrintf("%s started, cannot start cmdline", runOpToString(m_runOperation).c_str());
SE_THROW_EXCEPTION(InvalidCall, msg);
}
//create ostream with a specified streambuf
m_cmdline.reset(new CmdlineWrapper(*this, args));
//args are checked before transferred to dbus server
m_cmdline->parse();
m_runOperation = OP_CMDLINE;
g_main_loop_quit(loop);
}
inline void insertPair(std::map<string, string> &params,
const string &key,
const string &value)
@ -4781,7 +4845,7 @@ gboolean DBusServer::connmanPoll (gpointer dbusserver)
}connman (conn);
typedef std::map <std::string, boost::variant <std::vector <std::string> > > PropDict;
DBusClientCall0<PropDict> getProp(connman);
DBusClientCall1<PropDict> getProp(connman);
getProp (boost::bind(&DBusServer::connmanCallback, me, _1, _2));
return TRUE;
}
@ -5094,7 +5158,7 @@ BluezManager::BluezManager(DBusServer &server) :
m_bluezConn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, true, NULL);
if(m_bluezConn) {
m_done = false;
DBusClientCall0<DBusObject_t> getAdapter(*this, "DefaultAdapter");
DBusClientCall1<DBusObject_t> getAdapter(*this, "DefaultAdapter");
getAdapter(boost::bind(&BluezManager::defaultAdapterCb, this, _1, _2 ));
m_adapterChanged.reset(new SignalWatch1<DBusObject_t>(*this, "DefaultAdapterChanged"));
(*m_adapterChanged)(boost::bind(&BluezManager::defaultAdapterChanged, this, _1));
@ -5130,7 +5194,7 @@ BluezManager::BluezAdapter::BluezAdapter(BluezManager &manager, const string &pa
: m_manager(manager), m_path(path), m_devNo(0), m_devReplies(0),
m_deviceRemoved(*this, "DeviceRemoved"), m_deviceAdded(*this, "DeviceCreated")
{
DBusClientCall0<std::vector<DBusObject_t> > listDevices(*this, "ListDevices");
DBusClientCall1<std::vector<DBusObject_t> > listDevices(*this, "ListDevices");
listDevices(boost::bind(&BluezAdapter::listDevicesCb, this, _1, _2));
//m_deviceRemoved.reset(new SignalWatch1<DBusObject_t>(*this, "DeviceRemoved"));
m_deviceRemoved(boost::bind(&BluezAdapter::deviceRemoved, this, _1));
@ -5182,7 +5246,7 @@ void BluezManager::BluezAdapter::deviceCreated(const DBusObject_t &object)
BluezManager::BluezDevice::BluezDevice (BluezAdapter &adapter, const string &path)
: m_adapter(adapter), m_path(path), m_reply(false), m_propertyChanged(*this, "PropertyChanged")
{
DBusClientCall0<PropDict> getProperties(*this, "GetProperties");
DBusClientCall1<PropDict> getProperties(*this, "GetProperties");
getProperties(boost::bind(&BluezDevice::getPropertiesCb, this, _1, _2));
m_propertyChanged(boost::bind(&BluezDevice::propertyChanged, this, _1, _2));

View file

@ -55,6 +55,21 @@ Cmdline::Cmdline(int argc, const char * const * argv, ostream &out, ostream &err
m_validSourceProps(SyncSourceConfig::getRegistry())
{}
Cmdline::Cmdline(const vector<string> &args, ostream &out, ostream &err) :
m_args(args),
m_out(out),
m_err(err),
m_validSyncProps(SyncConfig::getRegistry()),
m_validSourceProps(SyncSourceConfig::getRegistry())
{
m_argc = args.size();
m_argvArray.reset(new const char *[args.size()]);
for(int i = 0; i < m_argc; i++) {
m_argvArray[i] = m_args[i].c_str();
}
m_argv = m_argvArray.get();
}
bool Cmdline::parse()
{
int opt = 1;
@ -159,6 +174,22 @@ bool Cmdline::parse()
} else if(boost::iequals(m_argv[opt], "--keyring")||
boost::iequals(m_argv[opt], "-k")) {
m_keyring = true;
} else if(boost::iequals(m_argv[opt], "--use-daemon")) {
opt++;
if (opt >= m_argc) {
usage(true, string("missing parameter for ") + cmdOpt(m_argv[opt - 1]));
return false;
}
if(boost::iequals(m_argv[opt], "yes")) {
m_useDaemon = "yes";
} else if(boost::iequals(m_argv[opt], "no")) {
m_useDaemon = "no";
} else {
usage(true, string("parameter '") + m_restore + "' for " + cmdOpt(m_argv[opt - 1]) + " must be 'yes' or 'no'");
}
} else if(boost::iequals(m_argv[opt], "--monitor")||
boost::iequals(m_argv[opt], "-m")) {
m_monitor = true;
} else {
usage(false, string(m_argv[opt]) + ": unknown parameter");
return false;
@ -176,6 +207,35 @@ bool Cmdline::parse()
return true;
}
bool Cmdline::isSync()
{
//make sure command line arguments really try to run sync
if(m_usage || m_version) {
return false;
} else if(m_printServers || boost::trim_copy(m_server) == "?") {
return false;
} else if(m_printTemplates || m_dontrun) {
return false;
} else if(m_argc == 1 || (!m_useDaemon.empty() && m_argc == 3)) {
return false;
} else if(m_printConfig || m_remove) {
return false;
} else if (m_server == "" && m_argc > 1) {
return false;
} else if(m_configure || m_migrate) {
return false;
} else if(m_status || m_printSessions) {
return false;
} else if(!m_restore.empty()) {
return false;
} else if(m_dryrun) {
return false;
} else if(!m_run && (m_syncProps.size() || m_sourceProps.size())) {
return false;
}
return true;
}
bool Cmdline::run() {
// --dry-run is only supported by some operations.
// Be very strict about it and make sure it is off in all
@ -204,7 +264,7 @@ bool Cmdline::run() {
}
} else if (m_dontrun) {
// user asked for information
} else if (m_argc == 1) {
} else if (m_argc == 1 || (!m_useDaemon.empty() && m_argc == 3)) {
// no parameters: list databases and short usage
const SourceRegistry &registry(SyncSource::getSourceRegistry());
boost::shared_ptr<FilterConfigNode> sharedNode(new VolatileConfigNode());
@ -1102,6 +1162,10 @@ void Cmdline::usage(bool full, const string &error, const string &param)
" they were not stored there already:" << endl <<
" --keyring --configure --sync-property proxyPassword=foo" << endl <<
"" << endl <<
"--use-daemon [yes/no]" << endl <<
" Run operations in cooperation with the background sync daemon;" << endl <<
" enabled by default if it is installed" << endl <<
"" << endl <<
" When passwords were stored in the keyring, their value is set to '-'" << endl <<
" in the configuration. This means that when running a synchronization" << endl <<
" without the --keyring argument, the password has to be entered" << endl <<

View file

@ -27,6 +27,7 @@
using namespace std;
#include <boost/shared_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
@ -42,6 +43,7 @@ public:
* @param err stderr stream for error messages
*/
Cmdline(int argc, const char * const *argv, ostream &out, ostream &err);
Cmdline(const vector<string> &args, ostream &out, ostream &err);
/**
* parse the command line options
@ -52,6 +54,20 @@ public:
bool run();
string useDaemon() { return m_useDaemon; }
/** whether '--monitor' is set */
bool monitor() { return m_monitor; }
/** whether 'status' is set */
bool status() { return m_status; }
/* server name */
string getConfigName() { return m_server; }
/* check whether command line runs sync. It should be called after parsing. */
bool isSync();
protected:
class Bool {
public:
@ -62,10 +78,16 @@ protected:
bool m_value;
};
// vector to store strings for arguments
vector<string> m_args;
int m_argc;
const char * const * m_argv;
ostream &m_out, &m_err;
//array to store pointers of arguments
boost::scoped_array<const char *> m_argvArray;
Bool m_quiet;
Bool m_dryrun;
Bool m_status;
@ -81,6 +103,8 @@ protected:
Bool m_printSessions;
Bool m_dontrun;
Bool m_keyring;
Bool m_monitor;
string m_useDaemon;
FilterConfigNode::ConfigFilter m_syncProps, m_sourceProps;
const ConfigPropertyRegistry &m_validSyncProps;
const ConfigPropertyRegistry &m_validSourceProps;

View file

@ -200,6 +200,7 @@ class LoggerBase : public Logger
} \
} while(false)
#define SE_LOG_SHOW(_instance, _prefix, _format, _args...) SE_LOG(Logger::SHOW, _instance, _prefix, _format, ##_args)
#define SE_LOG_ERROR(_instance, _prefix, _format, _args...) SE_LOG(Logger::ERROR, _instance, _prefix, _format, ##_args)
#define SE_LOG_WARNING(_instance, _prefix, _format, _args...) SE_LOG(Logger::WARNING, _instance, _prefix, _format, ##_args)
#define SE_LOG_INFO(_instance, _prefix, _format, _args...) SE_LOG(Logger::INFO, _instance, _prefix, _format, ##_args)

View file

@ -421,6 +421,14 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
*/
static void resetSignals() { s_flags = SuspendFlags(); }
/**
* handleSignals() is called in a signal handler,
* which can only call reentrant functions. Our
* logging code is not reentrant and thus has
* to be called outside of the signal handler.
*/
static void printSignals();
bool getRemoteInitiated() {return m_remoteInitiated;}
void setRemoteInitiated(bool remote) {m_remoteInitiated = remote;}
@ -716,14 +724,6 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
*/
string getSynthesisDatadir() { return getRootPath() + "/.synthesis"; }
/**
* handleSignals() is called in a signal handler,
* which can only call reentrant functions. Our
* logging code is not reentrant and thus has
* to be called outside of the signal handler.
*/
static void printSignals();
/**
* return true if "delayedabort" session variable is true
*/

View file

@ -38,6 +38,24 @@ using namespace std;
#include <dlfcn.h>
#include <syncevo/declarations.h>
#ifdef DBUS_SERVICE
#include <gdbus-cxx-bridge.h>
struct SourceStatus {
string m_mode;
string m_status;
uint32_t m_error;
};
template<> struct dbus_traits<SourceStatus> :
public dbus_struct_traits<SourceStatus,
dbus_member<SourceStatus, string, &SourceStatus::m_mode,
dbus_member<SourceStatus, string, &SourceStatus::m_status,
dbus_member_single<SourceStatus, uint32_t, &SourceStatus::m_error> > > >
{};
#endif
SE_BEGIN_CXX
#if defined(ENABLE_MAEMO) && defined (ENABLE_EBOOK)
@ -83,6 +101,244 @@ class KeyringSyncCmdline : public Cmdline {
}
};
#ifdef DBUS_SERVICE
class RemoteSession;
typedef map<string, StringMap> Config_t;
/**
* Act as a dbus server. All requests to dbus server
* are passed through this class.
*/
class RemoteDBusServer : public DBusRemoteObject
{
public:
RemoteDBusServer();
virtual const char *getDestination() const {return "org.syncevolution";}
virtual const char *getPath() const {return "/org/syncevolution/Server";}
virtual const char *getInterface() const {return "org.syncevolution.Server";}
virtual DBusConnection *getConnection() const {return m_conn.get();}
GMainLoop *getLoop() { return m_loop; }
/**
* check whether the server is started and can be attached.
* If not, report an error message
*/
bool checkStarted();
/**
* execute arguments from command line
* @param args the arguments of command line
* @param config the config name parsed from arguments if has
* @param runSync arguments to run a sync
* @return true if successfully
*/
bool execute(const vector<string> &args, const string &config, bool runSync);
/**
* To implement the feature of '--monitor' option, monitor a
* given config if there is a session running.
* If config is empty, then peak a running session to monitor.
* @param config the config name parsed from arguments if has
* @return true if successfully
*/
bool monitor(const string &config);
/**
* To implement the feature of '--status' without a server.
* get and print all running sessions in the dbus server
*/
bool runningSessions();
/** whether the dbus call(s) has/have completed */
bool done() { return m_replyTotal == m_replyCounter; }
/** one reply returns. Increase reply counter. */
void replyInc();
/** set whether there is an error */
void setResult(bool result) { m_result = result; }
private:
/** call 'Attach' until it returns */
void attachSync();
/** callback of 'Server.Attach' */
void attachCb(const string &error);
/** callback of 'Server.GetSessions' */
void getSessionsCb(const vector<string> &sessions, const string &error);
/** callback of 'Server.SessionChanged' */
void sessionChangedCb(const DBusObject_t &object, bool active);
/** callback of 'Server.LogOutput' */
void logOutputCb(const DBusObject_t &object, uint32_t level, const string &log);
/** callback of calling 'Server.StartSession' */
void startSessionCb(const DBusObject_t &session, const string &error);
/** update active session vector according to 'SessionChanged' signal */
void updateSessions(const string &session, bool active);
/** check m_session is active */
bool isActive();
/** get all running sessions. Used internally. */
void getRunningSessions();
/** set the total number of replies we must wait */
void resetReplies(int total = 1)
{
m_replyTotal = total;
m_replyCounter = 0;
}
/** signal handler for 'CTRL-C' */
static void handleSignal(int sig);
// session used for signal handler,
// used to call 'suspend' and 'abort'
static boost::shared_ptr<RemoteSession> g_session;
// the main loop
GMainLoop *m_loop;
// connection
DBusConnectionPtr m_conn;
// whether client can attach to the daemon.
// It is also used to indicate whether daemon is ready to use.
bool m_attached;
// error flag
bool m_result;
// config name
string m_configName;
// active session object path
boost::shared_ptr<string> m_activeSession;
// session created or monitored
boost::shared_ptr<RemoteSession> m_session;
// active sessions after listening to 'SessionChanged' signals
vector<string> m_activeSessions;
// all sessions in dbus server
vector<boost::shared_ptr<RemoteSession> > m_sessions;
// the number of total dbus calls
unsigned int m_replyTotal;
// the number of returned dbus calls
unsigned int m_replyCounter;
// sessions which are running
vector<boost::weak_ptr<RemoteSession> > m_runSessions;
// listen to dbus server signal 'SessionChanged'
boost::shared_ptr<SignalWatch2<DBusObject_t, bool> > m_sessionChanged;
// listen to dbus server signal 'LogOutput'
boost::shared_ptr<SignalWatch3<DBusObject_t, uint32_t, string> > m_logOutput;
};
/**
* Act as a session. All requests to a session are passed
* through this class.
*/
class RemoteSession : public DBusRemoteObject
{
public:
RemoteSession(RemoteDBusServer &server, const std::string &path);
virtual const char *getDestination() const {return "org.syncevolution";}
virtual const char *getPath() const {return m_path.c_str();}
virtual const char *getInterface() const {return "org.syncevolution.Session";}
virtual DBusConnection *getConnection() const {return m_server.getConnection();}
/**
* call 'Execute' method of 'Session' in dbus server
* without waiting for return
*/
void executeAsync(const vector<string> &args);
/**
* call 'GetStatus' method of 'Session' in dbus server
* without waiting for return
*/
void getStatusAsync();
/**
* call 'Suspend' method of 'Session' in dbus server
* without waiting for return
*/
void suspendAsync();
/**
* call 'Abort' method of 'Session' in dbus server
* without waiting for return
*/
void abortAsync();
/**
* call 'GetConfig' method of 'Session' in dbus server
* without waiting for return
*/
void getConfigAsync();
/** get config name of this session */
string configName() { return m_configName; }
/** status 'done' is sent by session */
bool statusDone() { return boost::iequals(m_status, "done"); }
/** get current status */
string status() { return m_status; }
/** monitor status of the sesion until it is done */
void monitorSync();
/** pass through logoutput and print them if m_output is true */
void logOutput(Logger::Level level, const string &log);
/** set whether to print output */
void setOutput(bool output) { m_output = output; }
typedef std::map<std::string, SourceStatus> SourceStatuses_t;
private:
/** callback of calling 'Session.Execute' */
void executeCb(const string &error);
/** callback of 'Session.GetStatus' */
void getStatusCb(const string &status,
uint32_t errorCode,
const SourceStatuses_t &sourceStatus,
const string &error);
/** callback of 'Session.GetConfig' */
void getConfigCb(const Config_t &config, const string &error);
/** callback of 'Session.StatusChanged' */
void statusChangedCb(const string &status,
uint32_t errorCode,
const SourceStatuses_t &sourceStatus);
/** callback of 'Session.Suspend' */
void suspendCb(const string &);
/** callback of 'Session.Abort' */
void abortCb(const string &);
/** dbus server */
RemoteDBusServer &m_server;
/* whether to log output */
bool m_output;
/** object path */
string m_path;
/** config name of the session */
string m_configName;
/** current status */
string m_status;
/** signal watch 'StatusChanged' */
SignalWatch3<std::string, uint32_t, SourceStatuses_t> m_statusChanged;
};
#endif
extern "C"
int main( int argc, char **argv )
{
@ -130,8 +386,6 @@ int main( int argc, char **argv )
free(exe);
try {
EDSAbiWrapperInit();
/*
* don't log errors to cerr: LogRedirect cannot distinguish
* between our valid error messages and noise from other
@ -139,12 +393,92 @@ int main( int argc, char **argv )
* level DEVELOPER, while output is at most INFO)
*/
KeyringSyncCmdline cmdline(argc, argv, std::cout, std::cout);
if (cmdline.parse() &&
cmdline.run()) {
return 0;
} else {
if(!cmdline.parse()) {
return 1;
}
if(cmdline.monitor()) {
#ifdef DBUS_SERVICE
// monitor a session
RemoteDBusServer server;
if(server.checkStarted() && server.monitor(cmdline.getConfigName())) {
return 0;
}
return 1;
#else
SE_LOG_SHOW(NULL, NULL, "ERROR: can't monitor running a session. "
"this syncevolution binary was compiled without support of daemon. "
"Try to re-configure with '--enable-dbus-service' option.");
return 1;
#endif
} else if(cmdline.status() &&
cmdline.getConfigName().empty()) {
#ifdef DBUS_SERVICE
// '--status' and no server name, try to get running sessions
RemoteDBusServer server;
if(server.checkStarted() && server.runningSessions()) {
return 0;
}
return 1;
#else
SE_LOG_SHOW(NULL, NULL, "ERROR: can't get all running sessions. "
"this syncevolution binary was compiled without support of daemon. "
"Try to re-configure with '--enable-dbus-service' option.");
return 1;
#endif
} else if(boost::iequals(cmdline.useDaemon(), "yes") ||
cmdline.useDaemon().empty()){
#ifdef DBUS_SERVICE
RemoteDBusServer server;
vector<string> arguments;
for(int i = 0; i < argc; i++) {
arguments.push_back(argv[i]);
}
bool result;
result = server.execute(arguments, cmdline.getConfigName(), cmdline.isSync());
//if '--use-daemon' is not set as 'yes' and can't execute
//successfully using daemon, falling back to run sync in the process
if(boost::iequals(cmdline.useDaemon(), "yes") && result == false) {
SE_LOG_SHOW(NULL, NULL, "ERROR: this syncevolution can't run by using daemon. "
"Either run syncevolution with '--use-daemon no' or without option.");
return 1;
} else if(result == true) {
return 0;
} else if(cmdline.useDaemon().empty()) {
SE_LOG_SHOW(NULL, NULL, "WARNING: can't run syncevolution with daemon. "
"Try to run your arguments directly.");
}
#else
if(boost::iequals(cmdline.useDaemon(), "yes")) {
SE_LOG_SHOW(NULL, NULL, "ERROR: this syncevolution binary was compiled without support of daemon. "
"Either run syncevolution with '--use-daemon no' or without option.");
}
#endif
}
// if forcing not using daemon or trying to use daemon with failures,
// run arguments in the process
if(cmdline.useDaemon().empty() ||
boost::iequals(cmdline.useDaemon(), "no")) {
EDSAbiWrapperInit();
/*
* don't log errors to cerr: LogRedirect cannot distinguish
* between our valid error messages and noise from other
* libs, therefore it would get suppressed (logged at
* level DEVELOPER, while output is at most INFO)
*/
if (cmdline.run()) {
return 0;
} else {
return 1;
}
}
} catch ( const std::exception &ex ) {
SE_LOG_ERROR(NULL, NULL, "%s", ex.what());
} catch (...) {
@ -154,4 +488,497 @@ int main( int argc, char **argv )
return 1;
}
#ifdef DBUS_SERVICE
/********************** RemoteDBusServer implementation **************************/
RemoteDBusServer::RemoteDBusServer()
:m_attached(false), m_result(true),
m_replyTotal(0), m_replyCounter(0)
{
m_loop = g_main_loop_new (NULL, FALSE);
m_conn = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, true, NULL);
if(m_conn) {
//check whether we can attach to the daemon
attachSync();
if(m_attached) {
m_sessionChanged.reset(new SignalWatch2<DBusObject_t, bool>(*this,"SessionChanged"));
(*m_sessionChanged)(boost::bind(&RemoteDBusServer::sessionChangedCb, this, _1, _2));
m_logOutput.reset(new SignalWatch3<DBusObject_t, uint32_t, string>(*this,"LogOutput"));
(*m_logOutput)(boost::bind(&RemoteDBusServer::logOutputCb, this, _1, _2, _3));
}
}
}
bool RemoteDBusServer::checkStarted()
{
if(!m_attached) {
SE_LOG_SHOW(NULL, NULL,"The Daemon can't be started successfully. Try to check it is successfully setup.");
return false;
}
return true;
}
void RemoteDBusServer::attachSync()
{
resetReplies();
DBusClientCall0 attach(*this, "Attach");
attach(boost::bind(&RemoteDBusServer::attachCb, this, _1));
while(!done()) {
g_main_loop_run(m_loop);
}
}
void RemoteDBusServer::attachCb(const string &error)
{
replyInc();
if(error.empty()) {
// don't print error information, leave it to caller
m_attached = true;
}
}
void RemoteDBusServer::logOutputCb(const DBusObject_t &object,
uint32_t level,
const string &log)
{
if(m_session &&
(boost::equals(object.c_str(), getPath()) ||
boost::equals(m_session->getPath(), object.c_str()))) {
m_session->logOutput((Logger::Level)level, log);
}
}
void RemoteDBusServer::sessionChangedCb(const DBusObject_t &object, bool active)
{
// update active sessions if needed
updateSessions(object, active);
g_main_loop_quit(m_loop);
}
boost::shared_ptr<RemoteSession> RemoteDBusServer::g_session;
void RemoteDBusServer::handleSignal(int sig)
{
SyncContext::handleSignal(sig);
if(g_session) {
const SuspendFlags &flags = SyncContext::getSuspendFlags();
if(flags.state == SuspendFlags::CLIENT_SUSPEND) {
g_session->suspendAsync();
} else if(flags.state == SuspendFlags::CLIENT_ABORT) {
g_session->abortAsync();
}
}
}
bool RemoteDBusServer::execute(const vector<string> &args, const string &peer, bool runSync)
{
//the basic workflow is:
//1) start a session
//2) waiting for the session becomes active
//3) execute 'arguments' once it is active
// start a new session
DBusClientCall1<DBusObject_t> call(*this, "StartSession");
call(peer, boost::bind(&RemoteDBusServer::startSessionCb, this, _1, _2));
// wait until 'StartSession' returns
resetReplies();
while(!done()) {
g_main_loop_run(m_loop);
}
if(m_session) {
//if session is not active, just wait
while(!isActive()) {
g_main_loop_run(m_loop);
}
Logger::Level level = LoggerBase::instance().getLevel();
LoggerBase::instance().setLevel(Logger::DEBUG);
resetReplies();
m_session->executeAsync(args);
while(!done()) {
g_main_loop_run(m_loop);
}
//if encoutering errors, return
if(!m_result) {
return m_result;
}
//g_session is used to pass 'abort' or 'suspend' commands
//make sure session is ready to run
g_session = m_session;
//set up signal handlers to send 'suspend' or 'abort' to dbus server
//only do this once session is executing and can suspend and abort
struct sigaction new_action, old_action;
struct sigaction old_term_action;
if(runSync) {
memset(&new_action, 0, sizeof(new_action));
new_action.sa_handler = handleSignal;
sigemptyset(&new_action.sa_mask);
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler == SIG_DFL) {
sigaction(SIGINT, &new_action, NULL);
}
sigaction(SIGTERM, NULL, &old_term_action);
if (old_term_action.sa_handler == SIG_DFL) {
sigaction(SIGTERM, &new_action, NULL);
}
}
//wait until status is 'done'
while(!m_session->statusDone()) {
g_main_loop_run(m_loop);
}
if(runSync) {
sigaction (SIGINT, &old_action, NULL);
sigaction (SIGTERM, &old_term_action, NULL);
}
//reset session
g_session.reset();
//restore logging level
LoggerBase::instance().setLevel(level);
}
return m_result;
}
void RemoteDBusServer::startSessionCb(const DBusObject_t &sessionPath, const string &error)
{
replyInc();
if(!error.empty()) {
SE_LOG_SHOW(NULL, NULL, "ERROR: %s", error.c_str());
m_result = false;
g_main_loop_quit(m_loop);
return;
}
m_session.reset(new RemoteSession(*this, sessionPath));
g_main_loop_quit(m_loop);
}
bool RemoteDBusServer::isActive()
{
/** if current session is active and then start to call 'Execute' method */
if(m_session) {
BOOST_FOREACH(const string &session, m_activeSessions) {
if(boost::equals(m_session->getPath(), session.c_str())) {
return true;
}
}
}
return false;
}
void RemoteDBusServer::getRunningSessions()
{
//get all sessions
DBusClientCall1<vector<string> > sessions(*this, "GetSessions");
sessions(boost::bind(&RemoteDBusServer::getSessionsCb, this, _1, _2));
resetReplies();
while(!done()) {
g_main_loop_run(m_loop);
}
// get status of each session
resetReplies(m_sessions.size());
BOOST_FOREACH(boost::shared_ptr<RemoteSession> &session, m_sessions) {
session->getStatusAsync();
}
// waiting for all sessions 'GetStatus'
while(!done()) {
g_main_loop_run(m_loop);
}
// collect running sessions
BOOST_FOREACH(boost::shared_ptr<RemoteSession> &session, m_sessions) {
if(boost::istarts_with(session->status(), "running")) {
m_runSessions.push_back(boost::weak_ptr<RemoteSession>(session));
}
}
}
bool RemoteDBusServer::runningSessions()
{
//the basic working flow is:
//1) get all sessions
//2) check each session and collect running sessions
//3) get config name of running sessions and print them
getRunningSessions();
if(m_runSessions.empty()) {
SE_LOG_SHOW(NULL, NULL, "No running session(s) just now");
} else {
SE_LOG_SHOW(NULL, NULL, "Running session(s): ");
resetReplies(m_runSessions.size());
BOOST_FOREACH(boost::weak_ptr<RemoteSession> &session, m_runSessions) {
boost::shared_ptr<RemoteSession> lock = session.lock();
if(lock) {
lock->getConfigAsync();
}
}
//wait for 'GetConfig' returns
while(!done()) {
g_main_loop_run(m_loop);
}
// print all running sessions
BOOST_FOREACH(boost::weak_ptr<RemoteSession> &session, m_runSessions) {
boost::shared_ptr<RemoteSession> lock = session.lock();
if(!lock->configName().empty()) {
SE_LOG_SHOW(NULL, NULL, " %s (%s)", lock->configName().c_str(), lock->getPath());
}
}
}
return m_result;
}
void RemoteDBusServer::getSessionsCb(const vector<string> &sessions, const string &error)
{
replyInc();
if(!error.empty()) {
SE_LOG_SHOW(NULL, NULL, "ERROR: %s", error.c_str());
m_result = false;
g_main_loop_quit(m_loop);
return;
}
//create local objects for sessions
BOOST_FOREACH(const DBusObject_t &value, sessions) {
boost::shared_ptr<RemoteSession> session(new RemoteSession(*this, value));
m_sessions.push_back(session);
}
}
void RemoteDBusServer::updateSessions(const string &session, bool active)
{
if(active) {
//add it into active list
m_activeSessions.push_back(session);
} else {
//if inactive, remove it from active list
for(vector<string>::iterator it = m_activeSessions.begin();
it != m_activeSessions.end(); ++it) {
if(boost::equals(session, *it)) {
m_activeSessions.erase(it);
break;
}
}
}
}
void RemoteDBusServer::replyInc()
{
// increase counter and check whether all replies are returned
m_replyCounter++;
if(done()) {
g_main_loop_quit(m_loop);
}
}
bool RemoteDBusServer::monitor(const string &peer)
{
//the basic working flow is:
//1) get all sessions
//2) check each session and collect running sessions
//3) peak one session with the given peer and monitor it
getRunningSessions();
if(peer.empty()) {
//peak the first running sessions
BOOST_FOREACH(boost::weak_ptr<RemoteSession> &session, m_runSessions) {
boost::shared_ptr<RemoteSession> lock = session.lock();
if(lock) {
m_session = lock;
resetReplies();
m_session->getConfigAsync();
while(!done()) {
g_main_loop_run(m_loop);
}
m_session->monitorSync();
return m_result;
}
}
//if no running session
SE_LOG_SHOW(NULL, NULL, "No session is going to be monitored.");
} else {
string peerNorm = SyncConfig::normalizeConfigString(peer);
// get config names of running sessions
resetReplies(m_runSessions.size());
BOOST_FOREACH(boost::weak_ptr<RemoteSession> &session, m_runSessions) {
boost::shared_ptr<RemoteSession> lock = session.lock();
lock->getConfigAsync();
}
//wait for 'GetConfig' returns
while(!done()) {
g_main_loop_run(m_loop);
}
//find a session with the given name
vector<boost::shared_ptr<RemoteSession> >::iterator it = m_sessions.begin();
while(it != m_sessions.end()) {
string tempNorm = (*it)->configName();
if(boost::iequals(peerNorm, tempNorm)) {
m_session = *it;
//monitor the session status
m_session->monitorSync();
return m_result;
}
it++;
}
if(it == m_sessions.end()) {
SE_LOG_SHOW(NULL, NULL, "'%s' is not running", peer.c_str());
}
}
return m_result;
}
/********************** RemoteSession implementation **************************/
RemoteSession::RemoteSession(RemoteDBusServer &server,
const string &path)
:m_server(server), m_output(false), m_path(path),
m_statusChanged(*this, "StatusChanged")
{
m_statusChanged(boost::bind(&RemoteSession::statusChangedCb, this, _1, _2, _3));
}
void RemoteSession::executeAsync(const vector<string> &args)
{
//start to print outputs
m_output = true;
DBusClientCall0 call(*this, "Execute");
call(args, boost::bind(&RemoteSession::executeCb, this, _1));
}
void RemoteSession::executeCb(const string &error)
{
m_server.replyInc();
if(!error.empty()) {
SE_LOG_SHOW(NULL, NULL, "ERROR: %s", error.c_str());
m_server.setResult(false);
//end to print outputs
m_output = false;
return;
}
}
void RemoteSession::statusChangedCb(const string &status,
uint32_t errorCode,
const SourceStatuses_t &sourceStatus)
{
m_status = status;
if(status == "done") {
//if session is done, quit the loop
g_main_loop_quit(m_server.getLoop());
m_output = false;
}
}
void RemoteSession::getStatusAsync()
{
DBusClientCall3<string, uint32_t, SourceStatuses_t> call(*this, "GetStatus");
call(boost::bind(&RemoteSession::getStatusCb, this, _1, _2, _3, _4));
}
void RemoteSession::getStatusCb(const string &status,
uint32_t errorCode,
const SourceStatuses_t &sourceStatus,
const string &error)
{
m_server.replyInc();
if(!error.empty()) {
//ignore the error
return;
}
m_status = status;
}
void RemoteSession::getConfigAsync()
{
DBusClientCall1<Config_t> call(*this, "GetConfig");
call(false, boost::bind(&RemoteSession::getConfigCb, this, _1, _2));
}
void RemoteSession::getConfigCb(const Config_t &config, const string &error)
{
m_server.replyInc();
if(!error.empty()) {
//ignore the error
return;
}
// set config name
Config_t::const_iterator it = config.find("");
if(it != config.end()) {
StringMap global = it->second;
StringMap::iterator git = global.find("configName");
if(git != global.end()) {
m_configName = git->second;
}
}
}
void RemoteSession::suspendAsync()
{
DBusClientCall0 suspend(*this, "Suspend");
suspend(boost::bind(&RemoteSession::suspendCb, this, _1));
}
void RemoteSession::suspendCb(const string &error)
{
//avoid logging messages in handleSignal
SyncContext::printSignals();
if(!error.empty()) {
m_server.setResult(false);
}
}
void RemoteSession::abortCb(const string &error)
{
//avoid logging messages in handleSignal
SyncContext::printSignals();
if(!error.empty()) {
m_server.setResult(false);
}
}
void RemoteSession::abortAsync()
{
DBusClientCall0 abort(*this, "Abort");
abort(boost::bind(&RemoteSession::abortCb, this, _1));
}
void RemoteSession::logOutput(Logger::Level level, const string &log)
{
if(m_output) {
SE_LOG(level, NULL, NULL, "%s", log.c_str());
}
}
void RemoteSession::monitorSync()
{
m_output = true;
Logger::Level level = LoggerBase::instance().getLevel();
LoggerBase::instance().setLevel(Logger::DEBUG);
SE_LOG(Logger::SHOW, NULL, NULL, "Monitoring '%s' (%s)\n", m_configName.c_str(), getPath());
while(!statusDone()) {
g_main_loop_run(m_server.getLoop());
}
SE_LOG(Logger::SHOW, NULL, NULL, "Monitoring done");
LoggerBase::instance().setLevel(level);
m_output = false;
}
#endif
SE_END_CXX