569 lines
21 KiB
C++
569 lines
21 KiB
C++
/*
|
|
* Copyright (C) 2011 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
|
|
*/
|
|
|
|
#ifndef SESSION_H
|
|
#define SESSION_H
|
|
|
|
#include <syncevo/SynthesisEngine.h>
|
|
#include <boost/weak_ptr.hpp>
|
|
#include <boost/utility.hpp>
|
|
|
|
#include <gdbus-cxx-bridge.h>
|
|
|
|
#include <syncevo/SuspendFlags.h>
|
|
#include <syncevo/timeout.h>
|
|
|
|
#include "session-common.h"
|
|
#include "read-operations.h"
|
|
#include "progress-data.h"
|
|
#include "source-progress.h"
|
|
#include "source-status.h"
|
|
#include "timer.h"
|
|
#include "resource.h"
|
|
#include "dbus-callbacks.h"
|
|
|
|
SE_BEGIN_CXX
|
|
|
|
class Connection;
|
|
class Server;
|
|
class ForkExecParent;
|
|
class SessionProxy;
|
|
class InfoReq;
|
|
|
|
/**
|
|
* Represents and implements the Session interface. Use
|
|
* boost::shared_ptr to track it and ensure that there are references
|
|
* to it as long as the connection is needed.
|
|
*
|
|
* The actual implementation is split into two parts:
|
|
* - state as exposed via D-Bus is handled entirely in this class
|
|
* - syncing and command line execution run inside
|
|
* the forked syncevo-dbus-helper
|
|
*
|
|
* This allows creating and tracking a Session locally in
|
|
* syncevo-dbus-server and minimizes asynchronous calls into the
|
|
* helper. The helper is started on demand (which might be never,
|
|
* for simple sessions).
|
|
*/
|
|
class Session : public GDBusCXX::DBusObjectHelper,
|
|
public Logger,
|
|
public Resource,
|
|
private ReadOperations,
|
|
private boost::noncopyable
|
|
{
|
|
public:
|
|
/**
|
|
* the sync status for session
|
|
*/
|
|
enum SyncStatus {
|
|
SYNC_QUEUEING, ///< waiting to become ready for use
|
|
SYNC_IDLE, ///< ready, session is initiated but sync not started
|
|
SYNC_RUNNING, ///< sync is running
|
|
SYNC_ABORT, ///< sync is aborting
|
|
SYNC_SUSPEND, ///< sync is suspending
|
|
SYNC_DONE, ///< sync is done
|
|
SYNC_ILLEGAL
|
|
};
|
|
|
|
/** Part of D-Bus API. */
|
|
typedef std::map<std::string, SourceStatus> SourceStatuses_t;
|
|
/** Part of D-Bus API. */
|
|
typedef std::map<std::string, APISourceProgress> APISourceProgresses_t;
|
|
|
|
/** Internal, with automatic conversion to APISourceProgresses_t when needed. */
|
|
struct SourceProgresses_t : public std::map<std::string, SourceProgress> {
|
|
operator APISourceProgresses_t () const {
|
|
APISourceProgresses_t to;
|
|
to.insert(begin(), end());
|
|
return to;
|
|
}
|
|
};
|
|
|
|
private:
|
|
std::vector<std::string> m_flags;
|
|
const std::string m_sessionID;
|
|
std::string m_peerDeviceID;
|
|
|
|
/** Starts the helper, on demand (see useHelperAsync()). */
|
|
boost::shared_ptr<ForkExecParent> m_forkExecParent;
|
|
/** The D-Bus proxy for the helper. */
|
|
boost::shared_ptr<SessionProxy> m_helper;
|
|
|
|
/**
|
|
* Ensures that helper is running and that its D-Bus API is
|
|
* available via m_helper, then invokes the success
|
|
* callback. Startup errors are reported back via the error
|
|
* callback. It is the responsibility of that error callback to
|
|
* turn the session into the right failure state, usually via
|
|
* Session::failed(). Likewise, any unexpected failures or helper
|
|
* shutdowns need to be monitored by the caller of
|
|
* useHelperAsync(). useHelperAsync() merely logs these events.
|
|
*
|
|
* useHelperAsync() and its helper function, useHelper2(), are the
|
|
* ones called directly from the main event loop. They ensure that
|
|
* any exceptions thrown inside them, including exceptions thrown
|
|
* by the result.done(), are logged and turned into
|
|
* result.failed() calls.
|
|
*
|
|
* In practice, the helper is started at most once per session, to
|
|
* run the operation (see runOperation()). When it terminates, the
|
|
* session is either considered "done" or "failed", depending on
|
|
* whether the operation has completed already.
|
|
* @param env additional env variables to be set in the helper process
|
|
*/
|
|
void useHelperAsync(const SimpleResult &result, const StringMap &env = StringMap());
|
|
|
|
/**
|
|
* Finish the work started by useHelperAsync once helper has
|
|
* connected. The operation might still fail at this point.
|
|
*/
|
|
void useHelper2(const SimpleResult &result, const boost::signals2::connection &c);
|
|
|
|
/** set up m_helper */
|
|
void onConnect(const GDBusCXX::DBusConnectionPtr &conn) throw ();
|
|
/** unset m_helper but not m_forkExecParent (still processing signals) */
|
|
void onQuit(int result) throw ();
|
|
/** set after abort() and suspend(), to turn "child died' into the LOCERR_USERABORT status code */
|
|
void expectChildTerm(int result) throw ();
|
|
/** log failure */
|
|
void onFailure(SyncMLStatus status, const std::string &explanation) throw ();
|
|
/** log error output from helper */
|
|
void onOutput(const char *buffer, size_t length);
|
|
|
|
bool m_serverMode;
|
|
bool m_serverAlerted;
|
|
SharedBuffer m_initialMessage;
|
|
string m_initialMessageType;
|
|
|
|
boost::weak_ptr<Connection> m_connection;
|
|
std::string m_connectionError;
|
|
bool m_useConnection;
|
|
|
|
/** temporary config changes */
|
|
FilterConfigNode::ConfigFilter m_syncFilter;
|
|
FilterConfigNode::ConfigFilter m_sourceFilter;
|
|
SessionCommon::SourceFilters_t m_sourceFilters;
|
|
|
|
/** whether dbus clients set temporary configs */
|
|
bool m_tempConfig;
|
|
|
|
/**
|
|
* whether the dbus clients updated, removed or cleared configs,
|
|
* ignoring temporary configuration changes
|
|
*/
|
|
bool m_setConfig;
|
|
|
|
/** Session life cycle */
|
|
enum SessionStatus {
|
|
SESSION_IDLE, /**< not active yet, only Detach() allowed */
|
|
SESSION_ACTIVE, /**< active, config changes and Sync()/Execute() allowed */
|
|
SESSION_RUNNING, /**< one-time operation (Sync() or Execute()) in progress */
|
|
SESSION_DONE /**< operation completed, only Detach() still allowed */
|
|
};
|
|
SessionStatus m_status;
|
|
|
|
/**
|
|
* set when operation was aborted, enables special handling of "child quit" in onQuit().
|
|
*/
|
|
bool m_wasAborted;
|
|
|
|
/**
|
|
* True iff we initiated the sync.
|
|
*/
|
|
bool m_remoteInitiated;
|
|
SyncStatus m_syncStatus;
|
|
|
|
/** maps to names as used in D-Bus API */
|
|
inline std::string static syncStatusToString(SyncStatus state)
|
|
{
|
|
static const char * const strings[SYNC_ILLEGAL] = {
|
|
"queueing",
|
|
"idle",
|
|
"running",
|
|
"aborting",
|
|
"suspending",
|
|
"done"
|
|
};
|
|
return state >= SYNC_QUEUEING && state < SYNC_ILLEGAL ?
|
|
strings[state] :
|
|
"";
|
|
}
|
|
|
|
/** step info: whether engine is waiting for something */
|
|
bool m_stepIsWaiting;
|
|
|
|
/**
|
|
* Priority which determines position in queue.
|
|
* Lower is more important. PRI_DEFAULT is zero.
|
|
*/
|
|
int m_priority;
|
|
|
|
/** progress data, holding progress calculation related info */
|
|
ProgressData m_progData;
|
|
|
|
SourceStatuses_t m_sourceStatus;
|
|
|
|
uint32_t m_error;
|
|
SourceProgresses_t m_sourceProgress;
|
|
Timespec m_lastProgressTimestamp;
|
|
SourceProgresses_t m_lastProgress;
|
|
bool m_freeze;
|
|
|
|
// syncProgress() and sourceProgress() turn raw data from helper
|
|
// into usable information on D-Bus server side
|
|
void syncProgress(sysync::TProgressEventEnum type,
|
|
int32_t extra1, int32_t extra2, int32_t extra3);
|
|
void sourceProgress(sysync::TProgressEventEnum type,
|
|
const std::string &sourceName,
|
|
SyncMode sourceSyncMode,
|
|
int32_t extra1, int32_t extra2, int32_t extra3);
|
|
|
|
/** timer for fire status/progress usages */
|
|
Timer m_statusTimer;
|
|
Timer m_progressTimer;
|
|
|
|
/** the total number of sources to be restored */
|
|
int m_restoreSrcTotal;
|
|
/** the number of sources that have been restored */
|
|
int m_restoreSrcEnd;
|
|
|
|
/**
|
|
* Wrapper around useHelperAsync() which sets up the session
|
|
* to execute a specific operation (sync, command line, ...).
|
|
*/
|
|
void runOperationAsync(SessionCommon::RunOperation op,
|
|
const SuccessCb_t &helperReady,
|
|
const StringMap &env = StringMap());
|
|
|
|
/**
|
|
* A Session can be used for exactly one of the operations. This
|
|
* is the one. This gets set by the D-Bus method implementation
|
|
* which triggers the operation. All other D-Bus method
|
|
* implementations need to check it before allowing an operation
|
|
* or method call which would conflict or be illegal.
|
|
*/
|
|
SessionCommon::RunOperation m_runOperation;
|
|
|
|
/**
|
|
* If m_runOperation == OP_CMDLINE, then we need further information
|
|
* from the helper about the actual operation. We get that information
|
|
* via a sync progress signal with event == PEV_CUSTOM_START.
|
|
*/
|
|
SessionCommon::RunOperation m_cmdlineOp;
|
|
|
|
// parameters from syncExtended()
|
|
std::string m_syncMode;
|
|
StringMap m_syncEnv;
|
|
|
|
typedef std::map<std::string, SyncSourceReport> SyncSourceReports;
|
|
static void StoreSyncSourceReport(SyncSourceReports &reports, const std::string &name, const SyncSourceReport &report) { reports[name] = report; }
|
|
/** Recorded during a sync for getSyncSourceReport. */
|
|
SyncSourceReports m_syncSourceReports;
|
|
|
|
public:
|
|
SessionCommon::RunOperation getOperation() const { return m_runOperation; }
|
|
std::string getSyncMode() const { return m_syncMode; }
|
|
StringMap getSyncEnv() const { return m_syncEnv; }
|
|
|
|
/** Session.Attach() */
|
|
void attach(const GDBusCXX::Caller_t &caller);
|
|
|
|
/** Session.Detach() */
|
|
void detach(const GDBusCXX::Caller_t &caller);
|
|
|
|
/** Session.GetStatus() */
|
|
void getStatus(std::string &status,
|
|
uint32_t &error,
|
|
SourceStatuses_t &sources);
|
|
/** Session.GetProgress() */
|
|
void getAPIProgress(int32_t &progress,
|
|
APISourceProgresses_t &sources);
|
|
/** Internal, with more information. */
|
|
void getProgress(int32_t &progress,
|
|
SourceProgresses_t &sources);
|
|
|
|
/** Timestamp is set each time that m_progressSignal is triggered. */
|
|
Timespec getLastProgressTimestamp() const { return m_lastProgressTimestamp; }
|
|
/** SourceProgresses_t is set each time that m_progressSignal is triggered. */
|
|
void getLastProgress(Session::SourceProgresses_t &progress) const { progress = m_lastProgress; }
|
|
|
|
/** Session.Restore() */
|
|
void restore(const string &dir, bool before, const std::vector<std::string> &sources);
|
|
void restore2(const string &dir, bool before, const std::vector<std::string> &sources);
|
|
|
|
/** Session.checkPresence() */
|
|
void checkPresence (string &status);
|
|
|
|
/** Session.Execute() */
|
|
void execute(const vector<string> &args, const map<string, string> &vars);
|
|
void execute2(const vector<string> &args, const map<string, string> &vars);
|
|
|
|
private:
|
|
/**
|
|
* Must be called each time that properties changing the
|
|
* overall status are changed (m_syncStatus, m_error, m_sourceStatus).
|
|
* Ensures that the corresponding D-Bus signal is sent.
|
|
*
|
|
* Doesn't always send the signal immediately, because often it is
|
|
* likely that more status changes will follow shortly. To ensure
|
|
* that the "final" status is sent, call with flush=true.
|
|
*
|
|
* @param flush force sending the current status
|
|
*/
|
|
void fireStatus(bool flush = false);
|
|
/** like fireStatus() for progress information */
|
|
void fireProgress(bool flush = false);
|
|
|
|
/** Session.StatusChanged */
|
|
GDBusCXX::EmitSignal3<const std::string &,
|
|
uint32_t,
|
|
const SourceStatuses_t &> emitStatus;
|
|
/** Session.ProgressChanged */
|
|
GDBusCXX::EmitSignal2<int32_t,
|
|
const APISourceProgresses_t &> emitProgress;
|
|
public:
|
|
boost::signals2::signal<void (const std::string &,
|
|
uint32_t,
|
|
const SourceStatuses_t &)> m_statusSignal;
|
|
boost::signals2::signal<void (int32_t,
|
|
const SourceProgresses_t &)> m_progressSignal;
|
|
|
|
/**
|
|
* Sets minimum period of time in milliseconds between progress
|
|
* signals. Events coming to short after the previous one get
|
|
* skipped, to ensure that if an event gets emitted, it gets
|
|
* emitted at the time of the change.
|
|
*
|
|
* There is no guarantee that progress can be emitted very
|
|
* frequently (say, every second). In practice it gets updated at
|
|
* the end of processing each SyncML message.
|
|
*/
|
|
void setProgressTimeout(unsigned long ms) { m_progressTimer.setTimeout(ms); }
|
|
|
|
/**
|
|
* Sessions must always be held in a shared pointer
|
|
* because some operations depend on that. This
|
|
* constructor function here ensures that and
|
|
* also adds a weak pointer to the instance itself,
|
|
* so that it can create more shared pointers as
|
|
* needed.
|
|
*/
|
|
static boost::shared_ptr<Session> createSession(Server &server,
|
|
const std::string &peerDeviceID,
|
|
const std::string &config_name,
|
|
const std::string &session,
|
|
const std::vector<std::string> &flags = std::vector<std::string>());
|
|
|
|
/**
|
|
* automatically marks the session as completed before deleting it
|
|
*/
|
|
~Session();
|
|
|
|
/**
|
|
* explicitly mark an idle session as completed, even if it doesn't
|
|
* get deleted yet (exceptions not expected by caller)
|
|
*/
|
|
void done(bool success) throw () { doneCb(success); }
|
|
|
|
private:
|
|
Session(Server &server,
|
|
const std::string &peerDeviceID,
|
|
const std::string &config_name,
|
|
const std::string &session,
|
|
const std::vector<std::string> &flags = std::vector<std::string>());
|
|
boost::weak_ptr<Session> m_me;
|
|
boost::shared_ptr<InfoReq> m_passwordRequest;
|
|
void passwordRequest(const std::string &descr, const ConfigPasswordKey &key);
|
|
void sendViaConnection(const GDBusCXX::DBusArray<uint8_t> buffer,
|
|
const std::string &type,
|
|
const std::string &url);
|
|
void shutdownConnection();
|
|
void storeMessage(const GDBusCXX::DBusArray<uint8_t> &message,
|
|
const std::string &type);
|
|
void connectionState(const std::string &error);
|
|
|
|
/**
|
|
* Sends all messages via D-Bus, as if they came from the session
|
|
* helper. To be activated only temporarily while executing code
|
|
* in the server which is related to the session.
|
|
*/
|
|
virtual void messagev(const MessageOptions &options,
|
|
const char *format,
|
|
va_list args);
|
|
|
|
public:
|
|
enum {
|
|
PRI_CMDLINE = -10,
|
|
PRI_DEFAULT = 0,
|
|
PRI_CONNECTION = 10,
|
|
PRI_AUTOSYNC = 20
|
|
};
|
|
|
|
/**
|
|
* Default priority is 0. Higher means less important.
|
|
*/
|
|
void setPriority(int priority) { m_priority = priority; }
|
|
int getPriority() const { return m_priority; }
|
|
|
|
bool isServerAlerted() const { return m_serverAlerted; }
|
|
void setServerAlerted(bool serverAlerted) { m_serverAlerted = serverAlerted; }
|
|
|
|
void initServer(SharedBuffer data, const std::string &messageType);
|
|
void setStubConnection(const boost::shared_ptr<Connection> c) { m_connection = c; m_useConnection = static_cast<bool>(c); }
|
|
boost::weak_ptr<Connection> getStubConnection() { return m_connection; }
|
|
bool useStubConnection() { return m_useConnection; }
|
|
|
|
/**
|
|
* After the connection closes, the Connection instance is
|
|
* destructed immediately. This is necessary so that the
|
|
* corresponding cleanup can remove all other classes
|
|
* only referenced by the Connection.
|
|
*
|
|
* This leads to the problem that an active sync cannot
|
|
* query the final error code of the connection. This
|
|
* is solved by setting a generic error code here when
|
|
* the sync starts and overwriting it when the connection
|
|
* closes.
|
|
*/
|
|
void setStubConnectionError(const std::string &error) { m_connectionError = error; }
|
|
std::string getStubConnectionError() { return m_connectionError; }
|
|
|
|
Server &getServer() { return ReadOperations::m_server; }
|
|
std::string getConfigName() { return m_configName; }
|
|
std::string getSessionID() const { return m_sessionID; }
|
|
std::string getPeerDeviceID() const { return m_peerDeviceID; }
|
|
|
|
/** Session.GetFlags() */
|
|
std::vector<std::string> getFlags() { return m_flags; }
|
|
|
|
/** Session.GetConfigName() */
|
|
std::string getNormalConfigName() { return SyncConfig::normalizeConfigString(m_configName); }
|
|
|
|
/** Session.SetConfig() */
|
|
void setConfig(bool update, bool temporary,
|
|
const ReadOperations::Config_t &config);
|
|
|
|
/** Session.SetNamedConfig() */
|
|
void setNamedConfig(const std::string &configName,
|
|
bool update, bool temporary,
|
|
const ReadOperations::Config_t &config);
|
|
|
|
/** Session.Sync() */
|
|
void sync(const std::string &mode, const SessionCommon::SourceModes_t &sourceModes) {
|
|
syncExtended(mode, sourceModes, StringMap());
|
|
}
|
|
|
|
void syncExtended(const std::string &mode, const SessionCommon::SourceModes_t &sourceModes,
|
|
const StringMap &env);
|
|
|
|
/**
|
|
* finish the work started by sync once helper is ready (invoked
|
|
* by useHelperAsync() and thus may throw exceptions)
|
|
*/
|
|
void sync2(const std::string &mode, const SessionCommon::SourceModes_t &sourceModes);
|
|
|
|
/** Session.Abort() */
|
|
void abort();
|
|
|
|
/** abort active session, trigger result once done */
|
|
void abortAsync(const SimpleResult &result);
|
|
|
|
/** Session.Suspend() */
|
|
void suspend();
|
|
|
|
/** Change freeze state, trigger result once done. */
|
|
void setFreezeAsync(bool freeze, const Result<void (bool)> &result);
|
|
|
|
bool getFreeze() const { return m_freeze; }
|
|
|
|
/**
|
|
* step info for engine: whether the engine is blocked by something
|
|
* If yes, 'waiting' will be appended as specifiers in the status string.
|
|
* see GetStatus documentation.
|
|
*/
|
|
void setWaiting(bool isWaiting);
|
|
|
|
SyncStatus getSyncStatus() const { return m_syncStatus; }
|
|
|
|
/** session was just activated */
|
|
typedef boost::signals2::signal<void ()> SessionActiveSignal_t;
|
|
SessionActiveSignal_t m_sessionActiveSignal;
|
|
|
|
/** sync is successfully started */
|
|
typedef boost::signals2::signal<void ()> SyncSuccessStartSignal_t;
|
|
SyncSuccessStartSignal_t m_syncSuccessStartSignal;
|
|
|
|
/** sync completed (may have failed) */
|
|
typedef boost::signals2::signal<void (SyncMLStatus, SyncReport)> DoneSignal_t;
|
|
DoneSignal_t m_doneSignal;
|
|
|
|
/** a source was synced, emitted multiple times during a multi-cycle sync */
|
|
typedef boost::signals2::signal<void (const std::string &, const SyncSourceReport &)> SourceSyncedSignal_t;
|
|
SourceSyncedSignal_t m_sourceSynced;
|
|
|
|
/** gets latest SyncSourceReport of a source, returns false if none available yet */
|
|
bool getSyncSourceReport(const std::string &sourceName, SyncSourceReport &report) const;
|
|
|
|
/**
|
|
* Called by server when the session is ready to run.
|
|
* Only the session itself can deactivate itself.
|
|
*/
|
|
void activateSession();
|
|
|
|
/**
|
|
* Called by server when it has a password response for the
|
|
* session. The session ensures that it only has one pending
|
|
* request at a time, so these parameters are enough to identify
|
|
* the request.
|
|
*/
|
|
void passwordResponse(bool timedOut, bool aborted, const std::string &password);
|
|
|
|
void setRemoteInitiated (bool remote) { m_remoteInitiated = remote;}
|
|
private:
|
|
/** set m_syncFilter and m_sourceFilters to config */
|
|
virtual bool setFilters(SyncConfig &config);
|
|
|
|
void dbusResultCb(const std::string &operation, bool success, const SyncReport &report, const std::string &error) throw();
|
|
|
|
void setFreezeDone(bool changed, const std::string &error,
|
|
bool freeze,
|
|
const Result<void (bool)> &result);
|
|
|
|
/**
|
|
* to be called inside a catch() clause: returns error for any
|
|
* pending D-Bus method and then calls doneCb()
|
|
*/
|
|
void failureCb() throw();
|
|
|
|
/**
|
|
* explicitly mark the session as completed, even if it doesn't
|
|
* get deleted yet (invoked directly or indirectly from event
|
|
* loop and thus must not throw exceptions)
|
|
*
|
|
* @param success if false, then ensure that m_error is set
|
|
* before finalizing the session
|
|
* @param report valid only in case of success
|
|
*/
|
|
void doneCb(bool success, const SyncReport &report = SyncReport()) throw();
|
|
};
|
|
|
|
SE_END_CXX
|
|
|
|
#endif // SESSION_H
|