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:
parent
858d388f55
commit
55bd5f73db
16
src/src.am
16
src/src.am
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
|
Loading…
Reference in New Issue