/* * Copyright (C) 2005-2009 Patrick Ohly * Copyright (C) 2009 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 #include #include #include #include #include #include #include #include #include SE_BEGIN_CXX SuspendFlags::SuspendFlags() : m_level(Logger::INFO), m_state(NORMAL), m_receivedSignals(0), m_lastSuspend(0), m_senderFD(-1), m_receiverFD(-1), m_activeSignals(0) { } SuspendFlags::~SuspendFlags() { deactivate(); } SuspendFlags &SuspendFlags::getSuspendFlags() { // never free the instance, other singletons might depend on it static SuspendFlags *flags; if (!flags) { flags = new SuspendFlags; } return *flags; } static gboolean SignalChannelReadyCB(GIOChannel *source, GIOCondition condition, gpointer data) throw() { try { SuspendFlags &me = SuspendFlags::getSuspendFlags(); me.printSignals(); } catch (...) { Exception::handle(); } return TRUE; } /** * Own glib IO watch for file descriptor * which calls printSignals() */ class GLibGuard : public SuspendFlags::Guard { GIOChannel *m_channel; guint m_channelReady; public: GLibGuard(int fd) { // glib watch which calls printSignals() m_channel = g_io_channel_unix_new(fd); m_channelReady = g_io_add_watch(m_channel, G_IO_IN, SignalChannelReadyCB, NULL); } ~GLibGuard() { if (m_channelReady) { g_source_remove(m_channelReady); m_channelReady = 0; } if (m_channel) { g_io_channel_unref(m_channel); m_channel = NULL; } } }; SuspendFlags::State SuspendFlags::getState() const { if (m_abortBlocker.lock()) { // active abort blocker return ABORT; } else if (m_suspendBlocker.lock()) { // active suspend blocker return SUSPEND; } else { return m_state; } } bool SuspendFlags::isAborted() { printSignals(); return getState() == ABORT; } bool SuspendFlags::isSuspended() { printSignals(); return getState() == SUSPEND; } bool SuspendFlags::isNormal() { printSignals(); return getState() == NORMAL; } void SuspendFlags::checkForNormal() { printSignals(); if (getState() != NORMAL) { SE_THROW_EXCEPTION_STATUS(StatusException, "aborting as requested by user", (SyncMLStatus)sysync::LOCERR_USERABORT); } } boost::shared_ptr SuspendFlags::suspend() { return block(m_suspendBlocker); } boost::shared_ptr SuspendFlags::abort() { return block(m_abortBlocker); } boost::shared_ptr SuspendFlags::block(boost::weak_ptr &blocker) { State oldState = getState(); boost::shared_ptr res = blocker.lock(); if (!res) { res.reset(new StateBlocker); blocker = res; } State newState = getState(); // only alert receiving side if going from normal -> suspend // or suspend -> abort if (newState > oldState && m_senderFD >= 0) { unsigned char msg = newState; write(m_senderFD, &msg, 1); } // don't depend on pipes or detecting that change, alert // listeners directly if (newState != oldState) { m_stateChanged(*this); } return res; } boost::shared_ptr SuspendFlags::activate(uint32_t sigmask) { SE_LOG_DEBUG(NULL, "SuspendFlags: (re)activating, currently %s", m_senderFD > 0 ? "active" : "inactive"); if (m_senderFD > 0) { return m_guard.lock(); } int fds[2]; if (pipe(fds)) { SE_THROW(StringPrintf("allocating pipe for signals failed: %s", strerror(errno))); } // nonblocking, to avoid deadlocks when the pipe's buffer overflows fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK); fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK); m_senderFD = fds[1]; m_receiverFD = fds[0]; SE_LOG_DEBUG(NULL, "SuspendFlags: activating signal handler(s) with fds %d->%d", m_senderFD, m_receiverFD); for (int sig = 0; sig < 32; sig++) { if (sigmask & (1< guard(new GLibGuard(m_receiverFD)); m_guard = guard; return guard; } void SuspendFlags::deactivate() { SE_LOG_DEBUG(NULL, "SuspendFlags: deactivating fds %d->%d", m_senderFD, m_receiverFD); if (m_receiverFD >= 0) { for (int sig = 0; sig < 32; sig++) { if (m_activeSignals & (1<= 0) { unsigned char msg2[2] = { (unsigned char)(ABORT_MAX + sig), msg }; write(me.m_senderFD, msg2, msg == ABORT_MAX ? 1 : 2); } } void SuspendFlags::printSignals() { if (m_receiverFD >= 0) { unsigned char msg; while (read(m_receiverFD, &msg, 1) == 1) { SE_LOG_DEBUG(NULL, "SuspendFlags: read %d from fd %d", msg, m_receiverFD); const char *str = NULL; switch (msg) { case SUSPEND: str = "Asking to suspend...\nPress CTRL-C again quickly (within 2s) to stop immediately (can cause problems in the future!)"; break; case SUSPEND_AGAIN: str = "Suspend in progress...\nPress CTRL-C again quickly (within 2s) to stop immediately (can cause problems in the future!)"; break; case ABORT: str = "Aborting immediately ..."; break; case ABORT_AGAIN: str = "Already aborting as requested earlier ..."; break; default: { int sig = msg - ABORT_MAX; SE_LOG_DEBUG(NULL, "reveived signal %d", sig); m_receivedSignals |= 1<