syncevolution/src/syncevo/LocalTransportAgent.cpp

415 lines
15 KiB
C++
Raw Normal View History

support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
/*
* Copyright (C) 2010 Patrick Ohly
*
* 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 <syncevo/LocalTransportAgent.h>
#include <syncevo/SyncContext.h>
#include <syncevo/SyncML.h>
#include <syncevo/LogRedirect.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
#include <syncevo/declarations.h>
SE_BEGIN_CXX
/**
* SyncML message data. Header followed directly by data.
*/
struct SyncMLMessage
{
Message m_message;
char m_data[0];
};
class NoopAgentDestructor
{
public:
void operator () (TransportAgent *agent) throw() {}
};
LocalTransportAgent::LocalTransportAgent(SyncContext *server,
const std::string &clientContext,
void *loop) :
m_server(server),
m_clientContext(SyncConfig::normalizeConfigString(clientContext)),
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
m_loop(static_cast<GMainLoop *>(loop)),
m_status(INACTIVE),
m_receiveBufferSize(0),
m_receivedBytes(0),
m_pid(0)
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
{
}
LocalTransportAgent::~LocalTransportAgent()
{
if (m_pid) {
SE_LOG_DEBUG(NULL, NULL, "starting to wait for child process %ld in destructor", (long)m_pid);
int status;
pid_t res = waitpid(m_pid, &status, 0);
SE_LOG_DEBUG(NULL, NULL, "child %ld completed, status %d", (long)res, status);
}
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
}
void LocalTransportAgent::start()
{
int sockets[2];
// compare normalized context names to detect forbidden sync
// within the same context; they could be set up, but are more
// likely configuration mistakes
string peer, context;
SyncConfig::splitConfigString(m_clientContext, peer, context);
if (!peer.empty()) {
SE_THROW(StringPrintf("invalid local sync URL: '%s' references a peer config, should point to a context like @%s instead",
m_clientContext.c_str(),
context.c_str()));
}
if (m_clientContext == m_server->getContextName()) {
SE_THROW(StringPrintf("invalid local sync inside context '%s', need second context with different databases", context.c_str()));
}
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
if (socketpair(AF_LOCAL,
SOCK_STREAM,
0, sockets)) {
m_server->throwError("socketpair()", errno);
}
pid_t pid = fork();
switch (pid) {
case -1:
m_server->throwError("fork()", errno);
break;
case 0:
// child
close(sockets[0]);
m_messageFD = sockets[1];
run();
break;
default:
// parent
close(sockets[1]);
m_messageFD = sockets[0];
// first message must come from child
m_status = ACTIVE;
m_pid = pid;
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
break;
}
}
void LocalTransportAgent::run()
{
// If we did an exec here, we could start with a clean slate.
// But that forces us to pass all relevant parameters to some
// specific executable, which is more complicated than simply
// reading from the existing in-process variables. So let's
// try without exec, after some clean up.
// Remove writing into the parent's log file => implemented as
// removing every logger until we run into the parents LogRedirect
// instance. That instance needs to be remembered and flushed
// before this process may terminate.
int index = LoggerBase::numLoggers();
LogRedirect *redirect = NULL;
--index;
while (index >= 0 &&
!(redirect = dynamic_cast<LogRedirect *>(LoggerBase::loggerAt(index)))) {
LoggerBase::popLogger();
--index;
}
// Now run. Under no circumstances must we leave this function,
// because our caller is not prepared for running inside a forked
// process.
int res = 0;
try {
SE_LOG_DEBUG(NULL, NULL, "client is running");
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
// TODO: password and abort handling in a derived class
SyncContext client(std::string("source-config") + m_clientContext,
local sync: avoid confusion about what data is changed In local sync the terms "local" and "remote" (in SyncReport, "Data modified locally") do not always apply and can be confusing. Replaced with explicitly mentioning the context. The source name also no longer is unique. Extended in the local sync case (and only in that case) by adding a <context>/ prefix to the source name. Here is an example of the modified output: $ syncevolution google [INFO] @default/itodo20: inactive [INFO] @default/addressbook: inactive [INFO] @default/calendar+todo: inactive [INFO] @default/memo: inactive [INFO] @default/ical20: inactive [INFO] @default/todo: inactive [INFO] @default/file_calendar+todo: inactive [INFO] @default/file_vcard21: inactive [INFO] @default/vcard30: inactive [INFO] @default/text: inactive [INFO] @default/file_itodo20: inactive [INFO] @default/vcard21: inactive [INFO] @default/file_ical20: inactive [INFO] @default/file_vcard30: inactive [INFO] @google/addressbook: inactive [INFO] @google/memo: inactive [INFO] @google/todo: inactive [INFO] @google/calendar: starting normal sync, two-way Local data changes to be applied remotely during synchronization: *** @google/calendar *** after last sync | current data removed since last sync < > added since last sync ------------------------------------------------------------------------------- BEGIN:VCALENDAR BEGIN:VCALENDAR ... END:VCALENDAR END:VCALENDAR ------------------------------------------------------------------------------- [INFO] @google/calendar: sent 1/2 [INFO] @google/calendar: sent 2/2 Local data changes to be applied remotely during synchronization: *** @default/calendar *** no changes [INFO] @default/calendar: started [INFO] @default/calendar: updating "created in Google, online" [INFO] @default/calendar: updating "created in Google - mod2, online" [INFO] @google/calendar: started [INFO] @default/calendar: inactive [INFO] @google/calendar: normal sync done successfully Synchronization successful. Changes applied during synchronization: +---------------|-----------------------|-----------------------|-CON-+ | | @default | @google | FLI | | Source | NEW | MOD | DEL | ERR | NEW | MOD | DEL | ERR | CTS | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | calendar | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | disabled, 0 KB sent by client, 2 KB received | | item(s) in database backup: 3 before sync, 3 after it | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | start Mon Oct 25 10:03:24 2010, duration 0:13min | | synchronization completed successfully | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ Data modified @default during synchronization: *** @default/calendar *** before sync | after sync removed during sync < > added during sync ------------------------------------------------------------------------------- BEGIN:VCALENDAR BEGIN:VCALENDAR VERSION:2.0 VERSION:2.0 ... END:VCALENDAR END:VCALENDAR ------------------------------------------------------------------------------- pohly@pohly-mobl1:/tmp/syncevolution/src$ Synchronization successful. Changes applied during synchronization: +---------------|-----------------------|-----------------------|-CON-+ | | @google | @default | FLI | | Source | NEW | MOD | DEL | ERR | NEW | MOD | DEL | ERR | CTS | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | calendar | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | | two-way, 2 KB sent by client, 0 KB received | | item(s) in database backup: 2 before sync, 2 after it | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | start Mon Oct 25 10:03:24 2010, duration 0:13min | | synchronization completed successfully | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ Data modified @google during synchronization: *** @google/calendar *** no changes
2010-10-25 10:34:23 +02:00
m_server->getConfigName(),
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
m_server->getRootPath() + "/." + m_clientContext,
boost::shared_ptr<TransportAgent>(this, NoopAgentDestructor()),
true);
// Copy some changes from main config: this is the only way
// how they can be set temporarily during a sync.
// TODO: find a way to have separate temporary settings
// for both sides.
client.setLogLevel(m_server->getLogLevel(), true);
client.setUsername(m_server->getUsername(), true);
client.setPassword(m_server->getPassword(), true);
client.setPreventSlowSync(m_server->getPreventSlowSync(), true);
client.setPrintChanges(m_server->getPrintChanges(), true);
client.setDumpData(m_server->getDumpData(), true);
// disable all sources temporarily, will be enabled by next loop
BOOST_FOREACH(const string &targetName, client.getSyncSources()) {
SyncSourceNodes targetNodes = client.getSyncSourceNodes(targetName);
SyncSourceConfig targetSource(targetName, targetNodes);
targetSource.setSync("disabled", true);
}
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
// activate all sources in client targeted by main config,
// with right uri
BOOST_FOREACH(const string &sourceName, m_server->getSyncSources()) {
SyncSourceNodes nodes = m_server->getSyncSourceNodesNoTracking(sourceName);
SyncSourceConfig source(sourceName, nodes);
string sync = source.getSync();
if (sync != "disabled") {
string targetName = source.getURI();
SyncSourceNodes targetNodes = client.getSyncSourceNodes(targetName);
SyncSourceConfig targetSource(targetName, targetNodes);
string fullTargetName = m_clientContext + "/" + targetName;
if (!targetNodes.dataConfigExists()) {
client.throwError(StringPrintf("%s: source not configured",
fullTargetName.c_str()));
}
// All of the config setting is done as volatile,
// so none of the regular config nodes have to
// be written. If a sync mode was set, it must have been
// done before in this loop => error in original config.
if (string(targetSource.getSync()) != "disabled") {
client.throwError(StringPrintf("%s: source targetted twice by %s",
fullTargetName.c_str(),
m_clientContext.c_str()));
}
targetSource.setSync(sync.c_str(), true);
targetSource.setURI(sourceName.c_str(), true);
}
}
// now sync
client.sync(&m_clientReport);
} catch(...) {
SyncMLStatus status = m_clientReport.getStatus();
Exception::handle(&status, redirect);
m_clientReport.setStatus(status);
}
if (redirect) {
redirect->flush();
}
_exit(res);
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
}
void LocalTransportAgent::getClientSyncReport(SyncReport &report)
{
report = m_clientReport;
}
void LocalTransportAgent::setContentType(const std::string &type)
{
if (type == m_contentTypeSyncML) {
m_sendType = Message::MSG_SYNCML_XML;
} else if (type == m_contentTypeSyncWBXML) {
m_sendType = Message::MSG_SYNCML_WBXML;
} else {
SE_THROW(string("unsupported content type: ") + type);
}
}
void LocalTransportAgent::shutdown()
{
if (m_pid) {
// TODO: get sync report, then wait for exit in child.
close(m_messageFD);
int status;
SE_LOG_DEBUG(NULL, NULL, "starting to wait for child process %ld in shutdown()", (long)m_pid);
pid_t res = waitpid(m_pid, &status, 0);
SE_LOG_DEBUG(NULL, NULL, "child %ld completed, status %d", (long)res, status);
m_pid = 0;
} else {
close(m_messageFD);
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
}
}
void LocalTransportAgent::send(const char *data, size_t len)
{
if (m_loop) {
SE_THROW("glib support not implemented");
} else {
// first throw away old received message
if (m_receivedBytes >= sizeof(Message) &&
m_receiveBuffer->m_length <= m_receivedBytes) {
size_t len = m_receiveBuffer->m_length;
// memmove() probably never necessary because receiving
// ends directly after complete message, but doesn't hurt
// either...
memmove(m_receiveBuffer.get(),
(char *)m_receiveBuffer.get() + len,
m_receivedBytes - len);
m_receivedBytes -= len;
}
SyncMLMessage header;
header.m_message.m_type = m_sendType;
header.m_message.m_length = sizeof(Message) + len;
struct iovec vec[2];
vec[0].iov_base = &header;
vec[0].iov_len = offsetof(SyncMLMessage, m_data);
vec[1].iov_base = (void *)data;
vec[1].iov_len = len;
// TODO: handle timeouts and aborts while writing
SE_LOG_DEBUG(NULL, NULL, "%s: sending %ld bytes",
m_pid ? "parent" : "child",
(long)len);
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
if (writev(m_messageFD, vec, 2) == -1) {
SE_LOG_DEBUG(NULL, NULL, "%s: sending %ld bytes failed",
m_pid ? "parent" : "child",
(long)len);
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
SE_THROW_EXCEPTION(TransportException,
StringPrintf("writev(): %s", strerror(errno)));
}
SE_LOG_DEBUG(NULL, NULL, "%s: sending %ld bytes done",
m_pid ? "parent" : "child",
(long)len);
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
}
m_status = ACTIVE;
}
void LocalTransportAgent::cancel()
{
}
TransportAgent::Status LocalTransportAgent::wait(bool noReply)
{
switch (m_status) {
case ACTIVE:
// need next message; for noReply == true, it is the SyncReport (TODO)
if (noReply) {
// TODO: remove this code and send SyncReport as last message in client
m_status = INACTIVE;
return m_status;
}
if (m_loop) {
SE_THROW("glib support not implemented");
} else {
while (m_status == ACTIVE &&
(!m_receiveBufferSize ||
m_receiveBufferSize < sizeof(Message) ||
m_receivedBytes < m_receiveBuffer->m_length)) {
// use select to implement timeout (TODO)
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(m_messageFD, &readfds);
SE_LOG_DEBUG(NULL, NULL, "%s: waiting",
m_pid ? "parent" : "child");
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
int res = select(m_messageFD + 1, &readfds, NULL, NULL, NULL);
switch (res) {
case 0:
SE_LOG_DEBUG(NULL, NULL, "%s: select timeout",
m_pid ? "parent" : "child");
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
// TODO: timeout
SE_THROW("internal error, unexpected timeout");
break;
case 1: {
// data ready, ensure that buffer is available
if (!m_receiveBufferSize) {
m_receiveBufferSize = m_server->getMaxMsgSize();
m_receiveBuffer.set(static_cast<Message *>(malloc(m_receiveBufferSize)),
"Message Buffer");
} else if (m_receivedBytes >= sizeof(Message) &&
m_receiveBuffer->m_length > m_receiveBufferSize) {
m_receiveBuffer.set(static_cast<Message *>(realloc(m_receiveBuffer.release(), m_receiveBuffer->m_length)),
"Message Buffer");
}
SE_LOG_DEBUG(NULL, NULL, "%s: recv",
m_pid ? "parent" : "child");
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
ssize_t recvd = recv(m_messageFD,
(char *)m_receiveBuffer.get() + m_receivedBytes,
m_receiveBufferSize - m_receivedBytes,
MSG_DONTWAIT);
SE_LOG_DEBUG(NULL, NULL, "%s: received %ld",
m_pid ? "parent" : "child",
(long)recvd);
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
if (recvd < 0) {
SE_THROW_EXCEPTION(TransportException,
StringPrintf("message receive: %s", strerror(errno)));
} else if (!recvd) {
SE_THROW_EXCEPTION(TransportException,
"client has died unexpectedly");
}
m_receivedBytes += recvd;
break;
}
default:
SE_LOG_DEBUG(NULL, NULL, "%s: select errror: %s",
m_pid ? "parent" : "child",
strerror(errno));
support local sync (BMC #712) Local sync is configured with a new syncURL = local://<context> where <context> identifies the set of databases to synchronize with. The URI of each source in the config identifies the source in that context to synchronize with. The databases in that context run a SyncML session as client. The config itself is for a server. Reversing these roles is possible by putting the config into the other context. A sync is started by the server side, via the new LocalTransportAgent. That agent forks, sets up the client side, then passes messages back and forth via stream sockets. Stream sockets are useful because unexpected peer shutdown can be detected. Running the server side requires a few changes: - do not send a SAN message, the client will start the message exchange based on the config - wait for that message before doing anything The client side is more difficult: - Per-peer config nodes do not exist in the target context. They are stored in a hidden .<context> directory inside the server config tree. This depends on the new "registering nodes in the tree" feature. All nodes are hidden, because users are not meant to edit any of them. Their name is intentionally chosen like traditional nodes so that removing the config also removes the new files. - All relevant per-peer properties must be copied from the server config (log level, printing changes, ...); they cannot be set differently. Because two separate SyncML sessions are used, we end up with two normal session directories and log files. The implementation is not complete yet: - no glib support, so cannot be used in syncevo-dbus-server - no support for CTRL-C and abort - no interactive password entry for target sources - unexpected slow syncs are detected on the client side, but not reported properly on the server side
2010-07-31 18:28:53 +02:00
SE_THROW_EXCEPTION(TransportException,
StringPrintf("select(): %s", strerror(errno)));
break;
}
}
if (m_status == ACTIVE) {
// complete message received, check if it is SyncML
switch (m_receiveBuffer->m_type) {
case Message::MSG_SYNCML_XML:
case Message::MSG_SYNCML_WBXML:
m_status = GOT_REPLY;
break;
default:
// TODO: handle other types
SE_THROW("unsupported message type");
break;
}
}
}
break;
default:
return m_status;
}
return m_status;
}
void LocalTransportAgent::getReply(const char *&data, size_t &len, std::string &contentType)
{
if (m_status != GOT_REPLY) {
SE_THROW("internal error, no reply available");
}
switch (m_receiveBuffer->m_type) {
case Message::MSG_SYNCML_XML:
contentType = m_contentTypeSyncML;
break;
case Message::MSG_SYNCML_WBXML:
contentType = m_contentTypeSyncWBXML;
break;
default:
contentType = "";
SE_THROW("internal error, no the right message");
break;
}
if (!contentType.empty()) {
SyncMLMessage *msg = reinterpret_cast<SyncMLMessage *>(m_receiveBuffer.get());
len = m_receiveBuffer->m_length - offsetof(SyncMLMessage, m_data);
data = msg->m_data;
}
}
void LocalTransportAgent::setCallback (TransportCallback cb, void * udata, int interval)
{
// TODO: implement timeout mechanism
}
SE_END_CXX