local sync: execute 'syncevo-local-sync' on child side, communicate via D-Bus

Instead of forking and continuing to sync in the forked process
without an explicit exec(), exec() the 'syncevo-local-sync' helper in
the forked process. The syncevo-local-sync helper binary gets
installed into libexec. SYNCEVOLUTION_LIBEXEC_DIR must be set if that
helper is not installed yet, not in the PATH, or an old version would
be found without that env variable ("make" without "make install"
during development!).

Main advantage is the cleaner environment for running the child side
of local sync. Required for getting ActiveSync to work again (GDBus
GIO as used by recent activesyncd client libraries did not work in the
forked process without the exec()).

Full D-Bus communication gets established between parent and child.
The downside is the hard dependency of local sync on the D-Bus
libraries (not the daemon!).

D-Bus communication allowed implementing interactive password requests
from the child side through the parent to the UI using the parent,
something that wasn't implemented before.

The child asks its parent for the password, which in turn
passes the request to its SyncContext. This happens to work
when that SyncContext is a normal instance (reads from stdin,
the "syncevolution --daemon" case) and the syncevo-dbus-server
(sends out an Info Request signal and waits for a response).

The info request and response are handled in the blocking
askPassword() by polling the running mail loop, so the parent should
remain responsive. Overall it is still a pretty difficult setup; it
would be better if askPassword() was asynchronous.

Describing the required password also is sub-optimal: the sync-ui just
asks for a password in its current config (even though that might not
be the config which currently gets synced) and crashes if no config is
currently selected. The command line uses the description derived from
the property and config name, which is a bit technical, but at least
correct.

Syncing uses the child's error string as "first error" in the parent,
too, by logging it anew on the parent side. That puts it into the
parent's sync report ahead of any additional error that it might
encounter while the child shuts down. Also use the child's status when
available instead of a misleading TransportError.

In addition, suppress as many of these errors as possible when we know
already that the child reported an error in its sync report. Not all
"transport errors" are currently avoided that way, but this is at
least a first step.
This commit is contained in:
Patrick Ohly 2012-01-09 16:30:53 +01:00
parent 858d388f55
commit 55bd5f73db
5 changed files with 874 additions and 717 deletions

View File

@ -164,6 +164,22 @@ distclean-local:
rm -rf $(SYNTHESIS_SUBDIR)
rm -rf $(CLEAN_CLIENT_SRC)
# Local sync helper executable. Built unconditionally at the moment,
# thus creating a hard dependency on D-Bus.
libexec_PROGRAMS += src/syncevo-local-sync
src_syncevo_local_sync_SOURCES = \
src/syncevo-local-sync.cpp \
$(CORE_SOURCES)
if ENABLE_UNIT_TESTS
nodist_src_syncevo_local_sync_SOURCES = test/test.cpp
endif
src_syncevo_local_sync_LDADD = $(gdbus_build_dir)/libgdbussyncevo.la $(CORE_LDADD) $(KEYRING_LIBS) $(KDE_KWALLET_LIBS) $(DBUS_LIBS)
src_syncevo_local_sync_CPPFLAGS = -DHAVE_CONFIG_H -I$(gdbus_dir) $(src_cppflags)
src_syncevo_local_sync_CXXFLAGS = $(PCRECPP_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(CORE_CXXFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) $(LIBSOUP_CFLAGS) $(KEYRING_CFLAGS) $(KDE_KWALLET_CFLAGS) $(SYNCEVO_WFLAGS)
src_syncevo_local_sync_LDFLAGS = $(PCRECPP_LIBS) $(CORE_LD_FLAGS) $(LIBSOUP_LIBS)
src_syncevo_local_sync_DEPENDENCIES = $(builddir)/$(gdbus_build_dir)/libgdbussyncevo.la $(EXTRA_LTLIBRARIES) $(CORE_DEP) $(SYNTHESIS_DEP)
# Do the linking here, as with all SyncEvolution executables.
# Sources are compiled in dbus/server.
if COND_DBUS

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "config.h"
#include <syncevo/LocalTransportAgent.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
extern "C"
int main( int argc, char **argv )
{
return LocalTransportMain(argc, argv);
}
SE_END_CXX

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,13 @@
#include <syncevo/SyncML.h>
#include <syncevo/SmartPtr.h>
#include <syncevo/GLibSupport.h>
#include <syncevo/SyncConfig.h>
#include <syncevo/ForkExec.h>
// This needs to be defined before including gdbus-cxx-bridge.h!
#define DBUS_CXX_EXCEPTION_HANDLER SyncEvo::SyncEvoHandleException
#include "gdbus-cxx-bridge.h"
#include <string>
#include <stdint.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
@ -33,30 +39,13 @@ SE_BEGIN_CXX
class SyncContext;
/**
* Variable-sized message, used for both request and reply.
* Allocated with malloc/realloc().
* The main() function of the local transport helper.
* Implements the child side of local sync.
*/
struct Message
{
/** determines which kind of data follows after m_length */
enum Type {
MSG_SYNCML_XML,
MSG_SYNCML_WBXML,
MSG_PASSWORD_REQUEST,
MSG_PASSWORD_RESPONSE,
MSG_SYNC_REPORT
} m_type;
/** length including header */
size_t m_length;
/** payload */
char m_data[0];
/** length excluding header */
size_t getDataLength() const { return m_length - offsetof(Message, m_data); }
};
int LocalTransportMain(int argc, char **argv);
// internal in LocalTransportAgent.cpp
class LocalTransportChild;
/**
* message send/receive with a forked process as peer
@ -111,78 +100,43 @@ class LocalTransportAgent : public TransportAgent
private:
SyncContext *m_server;
boost::shared_ptr<SyncContext> m_client;
std::string m_clientContext;
GMainLoop *m_loop;
unsigned m_timeoutSeconds;
Status m_status;
/** type of message for next send() */
Message::Type m_sendType;
/** buffer for message, with total length of buffer as size */
class Buffer {
public:
Buffer() :
m_size(0),
m_used(0)
{}
/** actual message */
SmartPtr<Message *, Message *, UnrefFree<Message> > m_message;
/** number of allocated bytes */
size_t m_size;
/** number of valid bytes in buffer */
size_t m_used;
bool haveMessage();
} m_receiveBuffer;
/**
* Read/write stream socket for sending/receiving messages.
* Data is sent as struct Message (includes type and length),
* with per-type payload.
*/
int m_messageFD;
/**
* Second read/write stream socket for transferring final
* status. Same communication method as for m_messageFD.
* Necessary because the regular communication
* channel needs to be closed in case of a failure, to
* notify the peer.
*/
int m_statusFD;
/** 0 in client, child PID in server */
pid_t m_pid;
SyncReport m_clientReport;
void run();
GMainLoopCXX m_loop;
boost::shared_ptr<ForkExecParent> m_forkexec;
std::string m_contentType;
std::string m_replyContentType;
std::string m_replyMsg;
/**
* Write Message with given type into file descriptor.
* Retries until error or all data written.
*
* @return ACTIVE for success, TIME_OUT or FAILED for failure, exception for really bad error
* provides the D-Bus API expected by the forked process:
* - password requests
* - store the child's sync report
*/
Status writeMessage(int fd, Message::Type type, const char *data, size_t len, Timespec deadline);
boost::shared_ptr<GDBusCXX::DBusObjectHelper> m_parent;
/**
* Read bytes into buffer until complete Message
* is assembled. Will read additional bytes beyond
* end of that Message if available. An existing
* complete message is not overwritten.
* provides access to the forked process' D-Bus API
* - start sync (returns child's first message)
* - send server reply (returns child's next message or empty when done)
*
* @return ACTIVE for success, TIME_OUT or FAILED for failure, exception for really bad error
* Only non-NULL when child is running and connected.
*/
Status readMessage(int fd, Buffer &buffer, Timespec deadline);
boost::shared_ptr<LocalTransportChild> m_child;
/** utility function for parent: copy child's report into m_clientReport */
void receiveChildReport();
/** utility function for parent: check m_clientReport and log/throw errors */
void checkChildReport();
void onChildConnect(const GDBusCXX::DBusConnectionPtr &conn);
void onFailure(const std::string &error);
void onChildQuit(int status);
void askPassword(const std::string &passwordName,
const std::string &descr,
const ConfigPasswordKey &key,
const boost::shared_ptr< GDBusCXX::Result1<const std::string &> > &reply);
void storeSyncReport(const std::string &report);
void storeReplyMsg(const std::string &contentType,
const GDBusCXX::DBusArray<uint8_t> &reply,
const std::string &error);
/**
* utility function: calculate deadline for operation starting now

View File

@ -57,6 +57,7 @@ using namespace GDBusCXX;
#include <syncevo/Cmdline.h>
#include <syncevo/SyncContext.h>
#include <syncevo/LogRedirect.h>
#include <syncevo/LocalTransportAgent.h>
#include "CmdlineSyncClient.h"
#include <dlfcn.h>
@ -447,17 +448,20 @@ static void getEnvVars(map<string, string> &vars);
extern "C"
int main( int argc, char **argv )
{
if (boost::ends_with(argv[0], "syncevo-local-sync")) {
return LocalTransportMain(argc, argv);
}
// Intercept stderr and route it through our logging.
// stdout is printed normally. Deconstructing it when
// leaving main() does one final processing of pending
// output.
LogRedirect redirect(false);
SyncContext::initMain("syncevolution");
setvbuf(stderr, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
SyncContext::initMain("syncevolution");
// Expand PATH to cover the directory we were started from?
// This might be needed to find normalize_vcard.
char *exe = strdup(argv[0]);