]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Allow only a single instance of vmusr when multiple users are logged into a VM
authorOliver Kurth <okurth@vmware.com>
Mon, 17 Sep 2018 23:41:18 +0000 (16:41 -0700)
committerOliver Kurth <okurth@vmware.com>
Mon, 17 Sep 2018 23:41:18 +0000 (16:41 -0700)
When a vmusr process gets the "channel conflict" error while attempting
to open the toolbox-dnd channel, a channel reset is triggered.  That
reset results in the channel being restarted and a subsequent conflict
and reset occurs - every second until the channel becomes available.

For *nix guests:
The fix is making use of the repetitive channel resets where the only
RpcIn message received is a "reset" to catch this channel "permanently"
unavailable state.  If other RpcIn messages are received, a channel
is considered to be working and the cumulative error count is cleared..

lib/rpcin/rpcin.c:
- struct RpcIn: Added error status boolean and callback function to
                notify the dependent layer that a channel error has been
                resolved.
- RpcInLoop(): If a non "reset" message is received, clear any channel
               error status.  This will also notify the dependent layer
               that the channel is functioning.
- RpcIn_start(): Added additional argument for new callback; NULL if
                 not needed.

lib/rpcChannel/rpcChannel.c:
- struct rpcChannelInt:
  - Renamed "rpcErrorCount" to "rpcResetErrorCount" since it is actually
    a count of the consecutive channel reset failures and not a count
    of RpcChannel errors.
  - Added counter "rpcFailureCount" for the cumulative channel errors.
  - Added "rpcFailureCb" for optional callback to notify the app of a
    "permanent" channel failure.
- New function RpcChannelClearError() for RpcIn to notify when the
  channel is working; to clear the rpcFailureCount .
- RpcChannel_Setup() - added two arguments for (1) an optional function
                       to be called when there is a channel failure
                       and (2) a failure count threshold.
                       These optional values are stored in the
                       RpcChannel structure being created.
- RpcChannelError(): Added logic to notify the calling app if the error
                     threshold has been reached and notify the app if a
                     callback was provided. A zero threshold signifies
                     the single vmusr limit should not be enforced.
                     (fix disable switch).

services/vmtoolsd/mainLoop.c:
- New function ToolsCore_GetVmusrLimit() to retrieve the channel error
  threshold default or over-ride from tools.conf.

services/vmtoolsd/toolsRpc.c:
- Added ToolsCoreAppChannelFail(): Callback for "permanent" channel
  connection failure.  A warning is logged based on whether another
  "vmtoolsd -n vmusr" is running or not and the process is terminated.
  On Mac OS, the process is terminated with exit(1) as an indication
  to launchd that the vmusr process should not automatically be
  restarted.

The current implementation uses the error callback only for the vmusr
server on Linux (*nix).
The default channel error limit is 5 (approx. 5 second), but is user
configurable in tools.conf.

   [vmusr]
   maxChannelAttempts = n    # where allowed n = 0, 3-15

When "maxChannelAttempts = 0" is used, the restriction to a single
running vmusr process is not enforced.   The existing behavior is
restored with all the accompanying VMX log spew.  This is essentially
a user configurable feature disablement switch.

open-vm-tools/lib/include/rpcin.h
open-vm-tools/lib/include/vmware/tools/guestrpc.h
open-vm-tools/lib/rpcChannel/rpcChannel.c
open-vm-tools/lib/rpcIn/rpcin.c
open-vm-tools/services/vmtoolsd/mainLoop.c
open-vm-tools/services/vmtoolsd/toolsCoreInt.h
open-vm-tools/services/vmtoolsd/toolsRpc.c

index 64b815514ea6abc9f1c84163b8aa97222c875fb0..5f06546e8095b7043394e71ea3d9263574bd6170 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
+ * Copyright (C) 2007-2018 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -33,6 +33,8 @@ extern "C" {
 
 typedef void RpcIn_ErrorFunc(void *clientData, char const *status);
 
+typedef void RpcIn_ClearErrorFunc(void *clientData);
+
 typedef struct RpcIn RpcIn;
 
 #if defined(VMTOOLS_USE_GLIB) /* { */
@@ -44,7 +46,9 @@ RpcIn *RpcIn_Construct(GMainContext *mainCtx,
                        gpointer clientData);
 
 Bool RpcIn_start(RpcIn *in, unsigned int delay,
-                 RpcIn_ErrorFunc *errorFunc, void *errorData);
+                 RpcIn_ErrorFunc *errorFunc,
+                 RpcIn_ClearErrorFunc *clearErrorFunc,
+                 void *errorData);
 
 #else /* } { */
 
@@ -66,7 +70,10 @@ RpcIn *RpcIn_Construct(DblLnkLst_Links *eventQueue);
 
 Bool RpcIn_start(RpcIn *in, unsigned int delay,
                  RpcIn_Callback resetCallback, void *resetClientData,
-                 RpcIn_ErrorFunc *errorFunc, void *errorData);
+                 RpcIn_ErrorFunc *errorFunc,
+                 RpcIn_ClearErrorFunc *clearErrorFunc,
+                 void *errorData);
+
 
 /*
  * Don't use this function anymore - it's here only for backwards compatibility.
index e6fee26d83fdb016b60ee99d94a7f7b59359431c..659fd2066d93c8f90a46745959e90715fdd6272e 100644 (file)
@@ -125,6 +125,15 @@ typedef void (*RpcChannelResetCb)(RpcChannel *chan,
                                   gboolean success,
                                   gpointer data);
 
+/**
+ * Signature for the application callback function when unable to establish
+ * an RpcChannel connection.
+ *
+ * @param[in]  _state     Client data.
+ */
+typedef void (*RpcChannelFailureCb)(gpointer _state);
+
+
 gboolean
 RpcChannel_Start(RpcChannel *chan);
 
@@ -160,7 +169,9 @@ RpcChannel_Setup(RpcChannel *chan,
                  GMainContext *mainCtx,
                  gpointer appCtx,
                  RpcChannelResetCb resetCb,
-                 gpointer resetData);
+                 gpointer resetData,
+                 RpcChannelFailureCb failureCb,
+                 guint maxFailures);
 
 void
 RpcChannel_RegisterCallback(RpcChannel *chan,
index e6c7e0db71fae6e5787e4951ccfa50ab76282079..06d0cec8d1a258007b2098138298c172995c4731 100644 (file)
@@ -52,7 +52,15 @@ typedef struct RpcChannelInt {
    RpcChannelResetCb       resetCb;
    gpointer                resetData;
    gboolean                rpcError;
-   guint                   rpcErrorCount;
+   guint                   rpcResetErrorCount;  /* channel reset failures */
+   /*
+    * The rpcFailureCount is a cumulative count of calls made to
+    * RpcChannelError().  When getting repeated channel failures,
+    * the channel is constantly stopped and restarted.
+    */
+   guint                   rpcFailureCount;  /* cumulative channel failures */
+   RpcChannelFailureCb     rpcFailureCb;
+   guint                   rpcMaxFailures;
 #endif
 } RpcChannelInt;
 
@@ -122,7 +130,7 @@ RpcChannelRestart(gpointer _chan)
    chanStarted = RpcChannel_Start(&chan->impl);
    g_static_mutex_unlock(&chan->impl.outLock);
    if (!chanStarted) {
-      Warning("Channel restart failed [%d]\n", chan->rpcErrorCount);
+      Warning("Channel restart failed [%d]\n", chan->rpcResetErrorCount);
       if (chan->resetCb != NULL) {
          chan->resetCb(&chan->impl, FALSE, chan->resetData);
       }
@@ -153,9 +161,9 @@ RpcChannelCheckReset(gpointer _chan)
    if (chan->rpcError) {
       GSource *src;
 
-      if (++(chan->rpcErrorCount) > channelTimeoutAttempts) {
+      if (++(chan->rpcResetErrorCount) > channelTimeoutAttempts) {
          Warning("Failed to reset channel after %u attempts\n",
-                 chan->rpcErrorCount - 1);
+                 chan->rpcResetErrorCount - 1);
          if (chan->resetCb != NULL) {
             chan->resetCb(&chan->impl, FALSE, chan->resetData);
          }
@@ -163,7 +171,7 @@ RpcChannelCheckReset(gpointer _chan)
       }
 
       /* Schedule the channel restart for 1 sec in the future. */
-      Debug(LGPFX "Resetting channel [%u]\n", chan->rpcErrorCount);
+      Debug(LGPFX "Resetting channel [%u]\n", chan->rpcResetErrorCount);
       src = g_timeout_source_new(1000);
       g_source_set_callback(src, RpcChannelRestart, chan, NULL);
       g_source_attach(src, chan->mainCtx);
@@ -173,7 +181,7 @@ RpcChannelCheckReset(gpointer _chan)
 
    /* Reset was successful. */
    Debug(LGPFX "Channel was reset successfully.\n");
-   chan->rpcErrorCount = 0;
+   chan->rpcResetErrorCount = 0;
    Debug(LGPFX "Clearing backdoor behavior ...\n");
    gVSocketFailed = FALSE;
 
@@ -428,6 +436,8 @@ exit:
  * @param[in]  appCtx      Application context.
  * @param[in]  resetCb     Callback for when a reset occurs.
  * @param[in]  resetData   Client data for the reset callback.
+ * @param[in]  failureCB   Callback for when the channel failure limit is hit.
+ * @param[in]  maxFailures Maximum channel failures allowed.
  */
 
 void
@@ -436,7 +446,9 @@ RpcChannel_Setup(RpcChannel *chan,
                  GMainContext *mainCtx,
                  gpointer appCtx,
                  RpcChannelResetCb resetCb,
-                 gpointer resetData)
+                 gpointer resetData,
+                 RpcChannelFailureCb failureCb,
+                 guint maxFailures)
 {
    size_t i;
    RpcChannelInt *cdata = (RpcChannelInt *) chan;
@@ -446,6 +458,8 @@ RpcChannel_Setup(RpcChannel *chan,
    cdata->mainCtx = g_main_context_ref(mainCtx);
    cdata->resetCb = resetCb;
    cdata->resetData = resetData;
+   cdata->rpcFailureCb = failureCb;
+   cdata->rpcMaxFailures = maxFailures;
 
    cdata->resetReg.name = "reset";
    cdata->resetReg.callback = RpcChannelReset;
@@ -513,6 +527,24 @@ RpcChannel_UnregisterCallback(RpcChannel *chan,
 }
 
 
+/**
+ * Callback function to clear the cumulative channel error count when RpcIn
+ * is able to establish a working connection following an error or reset.
+ *
+ * @param[in]  _chan       The RPC channel.
+ */
+
+static void
+RpcChannelClearError(void *_chan)
+{
+   RpcChannelInt *chan = _chan;
+
+   Debug(LGPFX " %s: Clearing cumulative RpcChannel error count; was %d\n",
+         __FUNCTION__, chan->rpcFailureCount);
+   chan->rpcFailureCount = 0;
+}
+
+
 /**
  * Error handling function for the RPC channel. Enqueues the "check reset"
  * function for running later, if it's not yet enqueued.
@@ -523,16 +555,32 @@ RpcChannel_UnregisterCallback(RpcChannel *chan,
 
 static void
 RpcChannelError(void *_chan,
-                 char const *status)
+                char const *status)
 {
    RpcChannelInt *chan = _chan;
+
    chan->rpcError = TRUE;
+
    /*
     * XXX: Workaround for PR 935520.
     * Revert the log call to Warning() after fixing PR 955746.
     */
    Debug(LGPFX "Error in the RPC receive loop: %s.\n", status);
 
+   /*
+    * If an RPC failure callback has been registered and the failure limit
+    * check has not been suppressed, check whether the RpcChannel failure
+    * limit has been reached.
+    */
+   if (chan->rpcFailureCb != NULL &&
+       chan->rpcMaxFailures > 0 &&
+       ++chan->rpcFailureCount >= chan->rpcMaxFailures) {
+      /* Maximum number of channel errors has been reached. */
+      Warning(LGPFX "RpcChannel failure count %d; calling the failure "
+                    "callback function.\n", chan->rpcFailureCount);
+      chan->rpcFailureCb(chan->resetData);
+   }
+
    if (chan->resetCheck == NULL) {
       chan->resetCheck = g_idle_source_new();
       g_source_set_callback(chan->resetCheck, RpcChannelCheckReset, chan, NULL);
@@ -596,6 +644,7 @@ RpcChannel_Destroy(RpcChannel *chan)
    cdata->resetCb = NULL;
    cdata->resetData = NULL;
    cdata->appCtx = NULL;
+   cdata->rpcFailureCb = NULL;
 
    g_free(cdata->appName);
    cdata->appName = NULL;
@@ -771,7 +820,8 @@ RpcChannel_Start(RpcChannel *chan)
 
 #if defined(NEED_RPCIN)
    if (chan->in != NULL && !chan->inStarted) {
-      ok = RpcIn_start(chan->in, RPCIN_MAX_DELAY, RpcChannelError, chan);
+      ok = RpcIn_start(chan->in, RPCIN_MAX_DELAY, RpcChannelError,
+                       RpcChannelClearError, chan);
       chan->inStarted = ok;
    }
 #endif
index c2c51583760523f1cfacba6d646dc616b5a8c103..8eea7d86b4b03a26b95cc0b47cfb1e8a6ef0de11 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 1998-2018 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -178,6 +178,13 @@ struct RpcIn {
     */
    Bool inLoop;     // RpcInLoop is running.
    Bool shouldStop; // Stop the channel the next time RpcInLoop exits.
+
+   /*
+    * RpcInConnErrorHandler called; cleared when a non "reset" reply has been
+    * received.
+    */
+   Bool errStatus;
+   RpcIn_ClearErrorFunc *clearErrorFunc;
 };
 
 static Bool RpcInSend(RpcIn *in, int flags);
@@ -1070,6 +1077,8 @@ RpcInConnErrorHandler(int err,             // IN
    Debug("RpcIn: Error in socket %d, closing connection: %s.\n",
          AsyncSocket_GetFd(asock), AsyncSocket_Err2String(err));
 
+   in->errStatus = TRUE;
+
    if (conn->connected) {
       RpcInCloseChannel(conn->in, errmsg);
    } else { /* the connection never gets connected */
@@ -1489,6 +1498,26 @@ exit:
 }
 
 
+/*
+ * RpcInClearErrorStatus --
+ *
+ *      Clear the errStatus indicator and if a callback has been registered,
+ *      notify the RpcChannel layer that an error condition has been cleared.
+ */
+
+static void
+RpcInClearErrorStatus(RpcIn *in) // IN
+{
+   if (in->errStatus) {
+      Debug("RpcIn: %s: Clearing errStatus\n", __FUNCTION__);
+      in->errStatus = FALSE;
+      if (in->clearErrorFunc != NULL) {
+         in->clearErrorFunc(in->errorData);
+      }
+   }
+}
+
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -1569,6 +1598,12 @@ RpcInLoop(void *clientData) // IN
    if (repLen) {
       char *s = ByteDump(reply, repLen);
       Debug("RpcIn: received %d bytes, content:\"%s\"\n", (int) repLen, s);
+
+      /* If reply is not a "reset", the channel is functioning. */
+      if (in->errStatus && strcmp(s, "reset") != 0) {
+         RpcInClearErrorStatus(in);
+      }
+
       if (!RpcInExecRpc(in, reply, repLen, &errmsg)) {
          goto error;
       }
@@ -1584,6 +1619,11 @@ RpcInLoop(void *clientData) // IN
          lastPrintMilli = now;
       }
 
+      /* RpcIn connection is working - receiving. */
+      if (in->errStatus) {
+         RpcInClearErrorStatus(in);
+      }
+
       /*
        * Nothing to execute
        */
@@ -1792,18 +1832,21 @@ error:
 
 #if defined(VMTOOLS_USE_GLIB)
 Bool
-RpcIn_start(RpcIn *in,                    // IN
-            unsigned int delay,           // IN
-            RpcIn_ErrorFunc *errorFunc,   // IN
-            void *errorData)              // IN
+RpcIn_start(RpcIn *in,                                // IN
+            unsigned int delay,                       // IN
+            RpcIn_ErrorFunc *errorFunc,               // IN
+            RpcIn_ClearErrorFunc *clearErrorFunc,     // IN
+            void *errorData)                          // IN
+
 #else
 Bool
-RpcIn_start(RpcIn *in,                    // IN
-            unsigned int delay,           // IN
-            RpcIn_Callback resetCallback, // IN
-            void *resetClientData,        // IN
-            RpcIn_ErrorFunc *errorFunc,   // IN
-            void *errorData)              // IN
+RpcIn_start(RpcIn *in,                                // IN
+            unsigned int delay,                       // IN
+            RpcIn_Callback resetCallback,             // IN
+            void *resetClientData,                    // IN
+            RpcIn_ErrorFunc *errorFunc,               // IN
+            RpcIn_ClearErrorFunc *clearErrorFunc,     // IN
+            void *errorData)                          // IN
 #endif
 {
    ASSERT(in);
@@ -1811,6 +1854,7 @@ RpcIn_start(RpcIn *in,                    // IN
    in->delay = 0;
    in->maxDelay = delay;
    in->errorFunc = errorFunc;
+   in->clearErrorFunc = clearErrorFunc;
    in->errorData = errorData;
 
    /* No initial result */
index eaab9a13ca14a8880971819b49b201a8a7f84ae2..f6eb6fbdea4e2b100daf1db07bb368a9c67116a7 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 2008-2018 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -33,6 +33,7 @@
 #include "conf.h"
 #include "guestApp.h"
 #include "serviceObj.h"
+#include "str.h"
 #include "system.h"
 #include "util.h"
 #include "vmcheck.h"
 #include "vmware/tools/utils.h"
 #include "vmware/tools/vmbackup.h"
 
+
+/*
+ * Establish the default and maximum vmusr RPC channel error limits
+ * that will be used to detect that the single allowed toolbox-dnd channel
+ * is not available.
+ */
+
+/*
+ * Lowest number of RPC channel errors to reasonably indicate that the
+ * single allowed toolbox-dnd channel is currently in use by another
+ * process.
+ */
+#define VMUSR_CHANNEL_ERR_MIN 3        /* approximately 3 secs. */
+
+/*
+ * The default number of vmusr channel errors before quitting the vmusr
+ * process start-up.
+ */
+#define VMUSR_CHANNEL_ERR_DEFAULT 5    /* approximately 5  secs. */
+
+/*
+ * Arbitrary upper vmusr channel error count limit.
+ */
+#define VMUSR_CHANNEL_ERR_MAX 15       /* approximately 15 secs. */
+
+#define CONFNAME_MAX_CHANNEL_ATTEMPTS "maxChannelAttempts"
+
+
 /*
  ******************************************************************************
  * ToolsCoreCleanup --                                                  */ /**
@@ -401,6 +430,44 @@ ToolsCore_DumpState(ToolsServiceState *state)
 }
 
 
+/**
+ * Return the RpcChannel failure threshold for the tools user service.
+ *
+ * @param[in]      state       The service state.
+ *
+ * @return  The RpcChannel failure limit for the user tools service.
+ */
+
+guint
+ToolsCore_GetVmusrLimit(ToolsServiceState *state)      // IN
+{
+   gint errorLimit = 0;      /* Special value 0 means no error threshold. */
+
+   if (TOOLS_IS_USER_SERVICE(state)) {
+      errorLimit = VMTools_ConfigGetInteger(state->ctx.config,
+                                            state->name,
+                                            CONFNAME_MAX_CHANNEL_ATTEMPTS,
+                                            VMUSR_CHANNEL_ERR_DEFAULT);
+
+      /*
+       * A zero value is allowed and will disable the single vmusr
+       * process restriction.
+       */
+      if (errorLimit != 0 &&
+          (errorLimit < VMUSR_CHANNEL_ERR_MIN ||
+           errorLimit > VMUSR_CHANNEL_ERR_MAX)) {
+         g_warning("%s: Invalid %s: %s (%d) specified in tools configuration; "
+                   "using default value (%d)\n", __FUNCTION__,
+                   state->name, CONFNAME_MAX_CHANNEL_ATTEMPTS,
+                   errorLimit, VMUSR_CHANNEL_ERR_DEFAULT);
+         errorLimit = VMUSR_CHANNEL_ERR_DEFAULT;
+      }
+   }
+
+   return errorLimit;
+}
+
+
 /**
  * Returns the name of the TCLO app name. This will only return non-NULL
  * if the service is either the tools "guestd" or "userd" service.
index 6d304a716c0452b9ba1c847be8791ef65c76039e..fab9ae7e60b844288aa265d49b18c9a7ea4ad0fa 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
+ * Copyright (C) 2008-2018 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -111,6 +111,9 @@ ToolsCore_DumpPluginInfo(ToolsServiceState *state);
 void
 ToolsCore_DumpState(ToolsServiceState *state);
 
+guint
+ToolsCore_GetVmusrLimit(ToolsServiceState *state);
+
 const char *
 ToolsCore_GetTcloName(ToolsServiceState *state);
 
index aaf6c1d6dbf8e5a8f2183753ad1759020a7b3481..21c3290bad6f510b0e77aeb39cec0fc6a686e2bd 100644 (file)
@@ -110,6 +110,73 @@ ToolsCoreCheckReset(RpcChannel *chan,
 }
 
 
+#if !defined(_WIN32)
+/**
+ * ToolsCoreAppChannelFail --
+ *
+ * Call-back function for RpcChannel to report that the RPC channel for the
+ * toolbox-dnd (vmusr) application cannot be acquired. This would signify
+ * that the channel is currently in use by another vmusr process.
+ *
+ * @param[in]  _state   The service state.
+ */
+
+static void
+ToolsCoreAppChannelFail(UNUSED_PARAM(gpointer _state))
+{
+   char *cmdGrepVmusrTools;
+#if !defined(__APPLE__)
+   ToolsServiceState *state = _state;
+#endif
+#if defined(__linux__) || defined(__FreeBSD__) || defined(sun)
+   static const char  *vmusrGrepExpr = "'vmtoolsd.*vmusr'";
+#if defined(sun)
+   static const char *psCmd = "ps -aef";
+#else
+   static const char *psCmd = "ps ax";     /* using BSD syntax */
+#endif
+#else  /* Mac OS */
+   static const char  *vmusrGrepExpr = "'vmware-tools-daemon.*vmusr'";
+   static const char *psCmd = "ps -ex";
+#endif
+
+   cmdGrepVmusrTools = Str_Asprintf(NULL, "%s | egrep %s | egrep -v 'grep|%d'",
+                                    psCmd, vmusrGrepExpr, (int) getpid());
+
+   /*
+    * Check if there is another vmtoolsd vmusr process running on the
+    * system and log the appropriate warning message before terminating
+    * this vmusr process.
+    */
+   if (system(cmdGrepVmusrTools) == 0) {
+      g_warning("Exiting the vmusr process. Another vmusr process is "
+                "currently running.\n");
+   } else {
+      g_warning("Exiting the vmusr process; unable to acquire the channel.\n");
+   }
+   free(cmdGrepVmusrTools);
+
+#if !defined(__APPLE__)
+   if (g_main_loop_is_running(state->ctx.mainLoop)) {
+      g_warning("Calling g_main_loop_quit() to terminate the process.\n");
+      g_main_loop_quit(state->ctx.mainLoop);
+   } else {
+      g_warning("Exiting the process.\n");
+      exit(1);
+   }
+#else  /* Mac OS */
+   /*
+    * On Mac OS X, always exit with non-zero status. This is a signal to
+    * launchd that the vmusr process had a "permanent" failure and should
+    * not be automatically restarted for this user session.
+    */
+   g_warning("Exiting the process.\n");
+   exit(1);
+#endif
+}
+#endif
+
+
 /**
  * Checks all loaded plugins for their capabilities, and sends the data to the
  * host. The code will try to send all capabilities, just logging errors as
@@ -322,12 +389,31 @@ ToolsCore_InitRpc(ToolsServiceState *state)
    }
 
    if (state->ctx.rpc) {
+
+      /*
+       * Default tools RpcChannel setup: No channel error threshold limit and
+       *                                 no notification callback function.
+       */
+      RpcChannelFailureCb failureCb = NULL;
+      guint errorLimit = 0;
+
+#if !defined(_WIN32)
+
+      /* For the *nix user service app. */
+      if (TOOLS_IS_USER_SERVICE(state)) {
+         failureCb = ToolsCoreAppChannelFail;
+         errorLimit = ToolsCore_GetVmusrLimit(state);
+      }
+#endif
+
       RpcChannel_Setup(state->ctx.rpc,
                        app,
                        mainCtx,
                        &state->ctx,
                        ToolsCoreCheckReset,
-                       state);
+                       state,
+                       failureCb,
+                       errorLimit);
 
       /* Register the "built in" RPCs. */
       for (i = 0; i < ARRAYSIZE(rpcs); i++) {