Implement grant and revoke device 11/32611/5
authorPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Fri, 19 Dec 2014 13:56:56 +0000 (14:56 +0100)
committerPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Tue, 23 Dec 2014 13:47:06 +0000 (14:47 +0100)
[Bug/Feature]   Was not implemented
[Cause]         N/A
[Solution]      N/A
[Verification]  Run tests, run grant/revoke device cli commands

Change-Id: I4cfa67d99cb45d4747cfd780776bc2340c5f4535

15 files changed:
cli/command-line-interface.cpp
cli/command-line-interface.hpp
cli/main.cpp
client/vasum-client-impl.cpp
client/vasum-client-impl.hpp
client/vasum-client.cpp
client/vasum-client.h
common/lxc/cgroup.cpp [new file with mode: 0644]
common/lxc/cgroup.hpp [new file with mode: 0644]
server/host-connection.cpp
server/host-connection.hpp
server/host-dbus-definitions.hpp
server/zones-manager.cpp
server/zones-manager.hpp
tests/unit_tests/client/ut-client.cpp

index 8120ca8..1222a86 100644 (file)
@@ -31,6 +31,7 @@
 #include <functional>
 #include <ostream>
 #include <iostream>
+#include <fcntl.h>
 
 using namespace std;
 
@@ -222,5 +223,28 @@ void lookup_zone_by_id(int pos, int argc, const char** argv)
     vsm_zone_free(zone);
 }
 
+void grant_device(int pos, int argc, const char** argv)
+{
+    using namespace std::placeholders;
+
+    if (argc <= pos + 2) {
+        throw runtime_error("Not enough parameters");
+    }
+
+    uint32_t flags = O_RDWR;
+    one_shot(bind(vsm_grant_device, _1, argv[pos + 1], argv[pos + 2], flags));
+}
+
+void revoke_device(int pos, int argc, const char** argv)
+{
+    using namespace std::placeholders;
+
+    if (argc <= pos + 2) {
+        throw runtime_error("Not enough parameters");
+    }
+
+    one_shot(bind(vsm_revoke_device, _1, argv[pos + 1], argv[pos + 2]));
+}
+
 } // namespace cli
 } // namespace vasum
index 789abd6..6a655d8 100644 (file)
@@ -25,6 +25,7 @@
 #define CLI_COMMAND_LINE_INTERFACE_HPP
 
 #include <map>
+#include <vector>
 #include <functional>
 #include <ostream>
 #include <string>
@@ -46,7 +47,7 @@ public:
     /**
      * @see CommandLineInterface::CommandLineInterface
      */
-    typedef std::map<std::string, std::string> ArgsSpec;
+    typedef std::vector<std::pair<std::string, std::string>> ArgsSpec;
 
     /**
      * Dummy constructor (for stl usage)
@@ -152,6 +153,20 @@ void unlock_zone(int pos, int argc, const char** argv);
  */
 void lookup_zone_by_id(int pos, int argc, const char** argv);
 
+/**
+ * Parses command line arguments and call vsm_grant_device
+ *
+ * @see vsm_grant_device
+ */
+void grant_device(int pos, int argc, const char** argv);
+
+/**
+ * Parses command line arguments and call vsm_revoke_device
+ *
+ * @see vsm_revoke_device
+ */
+void revoke_device(int pos, int argc, const char** argv);
+
 
 } // namespace cli
 } // namespace vasum
index b91d4e1..1afe134 100644 (file)
@@ -98,6 +98,24 @@ std::map<std::string, CommandLineInterface> commands = {
             "Prints informations about zone",
             {{"zone_id", "id zone name"}}
         }
+    },
+    {
+        "grant_device", {
+            grant_device,
+            "grant_device zone_id device_name",
+            "Grants access to the given device",
+            {{"zone_id", "id zone name"},
+             {"device_name", " device name"}}
+        }
+    },
+    {
+        "revoke_device", {
+            revoke_device,
+            "revoke_device zone_id device_name",
+            "Revokes access to the given device",
+            {{"zone_id", "id zone name"},
+             {"device_name", " device name"}}
+        }
     }
 };
 
index 749b155..0af5269 100644 (file)
@@ -515,16 +515,22 @@ VsmStatus Client::vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexc
     return signalUnsubscribe(subscriptionId);
 }
 
-VsmStatus Client::vsm_zone_grant_device(const char*, const char*, uint32_t) noexcept
+VsmStatus Client::vsm_grant_device(const char* id, const char* device, uint32_t flags) noexcept
 {
-    mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented");
-    return vsm_get_status();
+    assert(id);
+    assert(device);
+
+    GVariant* args_in = g_variant_new("(ssu)", id, device, flags);
+    return callMethod(HOST_INTERFACE, api::host::METHOD_GRANT_DEVICE, args_in);
 }
 
-VsmStatus Client::vsm_revoke_device(const char*, const char*) noexcept
+VsmStatus Client::vsm_revoke_device(const char* id, const char* device) noexcept
 {
-    mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented");
-    return vsm_get_status();
+    assert(id);
+    assert(device);
+
+    GVariant* args_in = g_variant_new("(ss)", id, device);
+    return callMethod(HOST_INTERFACE, api::host::METHOD_REVOKE_DEVICE, args_in);
 }
 
 VsmStatus Client::vsm_zone_get_netdevs(const char*, VsmArrayString*) noexcept
index 66ffa82..f545d77 100644 (file)
@@ -183,9 +183,9 @@ public:
     /**
      *  @see ::vsm_del_state_callback
      */
-    VsmStatus vsm_zone_grant_device(const char* id,
-                                      const char* device,
-                                      uint32_t flags) noexcept;
+    VsmStatus vsm_grant_device(const char* id,
+                               const char* device,
+                               uint32_t flags) noexcept;
 
     /**
      *  @see ::vsm_revoke_device
index 2102432..00a4dfb 100644 (file)
@@ -196,12 +196,12 @@ API VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscri
     return getClient(client).vsm_del_state_callback(subscriptionId);
 }
 
-API VsmStatus vsm_zone_grant_device(VsmClient client,
-                                      const char* id,
-                                      const char* device,
-                                      uint32_t flags)
+API VsmStatus vsm_grant_device(VsmClient client,
+                               const char* id,
+                               const char* device,
+                               uint32_t flags)
 {
-    return getClient(client).vsm_zone_grant_device(id, device, flags);
+    return getClient(client).vsm_grant_device(id, device, flags);
 }
 
 API VsmStatus vsm_revoke_device(VsmClient client, const char* id, const char* device)
index eb4cbba..eba04e6 100644 (file)
@@ -463,10 +463,10 @@ VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscriptio
  * @param[in] flags access flags
  * @return status of this function call
  */
-VsmStatus vsm_zone_grant_device(VsmClient client,
-                                  const char* zone,
-                                  const char* device,
-                                  uint32_t flags);
+VsmStatus vsm_grant_device(VsmClient client,
+                           const char* zone,
+                           const char* device,
+                           uint32_t flags);
 
 /**
  * Revoke access to device
diff --git a/common/lxc/cgroup.cpp b/common/lxc/cgroup.cpp
new file mode 100644 (file)
index 0000000..807ddff
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Lxc cgroup stuff
+ */
+
+
+#include "config.hpp"
+#include "logger/logger.hpp"
+#include "utils/fs.hpp"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+namespace vasum {
+namespace lxc {
+
+namespace {
+
+std::string flagsToPermissions(bool grant, uint32_t flags)
+{
+    if (!grant) {
+        return "rwm";
+    }
+    switch (flags) {
+    case O_RDWR:
+        return "rwm";
+    case O_RDONLY:
+        return "rm";
+    case O_WRONLY:
+        return "wm";
+    default:
+        return std::string();
+    }
+}
+
+} // namespace
+
+bool setCgroup(const std::string& zoneName,
+               const std::string& cgroupName,
+               const std::string& value)
+{
+    const std::string path = "/sys/fs/cgroup/devices/lxc/" + zoneName + "/" + cgroupName;
+    LOGD("Set '" << value << "' to " << path);
+    return utils::saveFileContent(path, value);
+}
+
+bool isDevice(const std::string& device)
+{
+    struct stat devStat;
+    if (::stat(device.c_str(), &devStat) == -1) {
+        LOGD("Cannot access: " << device);
+        return false;
+    }
+    if (!S_ISCHR(devStat.st_mode) && !S_ISBLK(devStat.st_mode)) {
+        LOGD("Not a device: " << device);
+        return false;
+    }
+    return true;
+}
+
+bool setDeviceAccess(const std::string& zoneName,
+                     const std::string& device,
+                     bool grant,
+                     uint32_t flags)
+{
+    struct stat devStat;
+    if (::stat(device.c_str(), &devStat) == -1) {
+        LOGD("Cannot access: " << device);
+        return false;
+    }
+
+    char type;
+    if (S_ISCHR(devStat.st_mode)) {
+        type = 'c';
+    } else if (S_ISBLK(devStat.st_mode)) {
+        type = 'b';
+    } else {
+        LOGD("Not a device: " << device);
+        return false;
+    }
+
+    std::string perm = flagsToPermissions(grant, flags);
+    if (perm.empty()) {
+        LOGD("Invalid flags");
+        return false;
+    }
+
+    int major = major(devStat.st_rdev);
+    int minor = minor(devStat.st_rdev);
+
+    char value[100];
+    snprintf(value, sizeof(value), "%c %d:%d %s", type, major, minor, perm.c_str());
+
+    std::string name = grant ? "devices.allow" : "devices.deny";
+    return setCgroup(zoneName, name, value);
+}
+
+
+} // namespace lxc
+} // namespace vasum
diff --git a/common/lxc/cgroup.hpp b/common/lxc/cgroup.hpp
new file mode 100644 (file)
index 0000000..2592054
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Lxc cgroup stuff
+ */
+
+#ifndef COMMON_LXC_CGROUP_HPP
+#define COMMON_LXC_CGROUP_HPP
+
+#include <string>
+
+namespace vasum {
+namespace lxc {
+
+bool setCgroup(const std::string& zoneName,
+               const std::string& cgroupName,
+               const std::string& value);
+
+bool isDevice(const std::string& device);
+
+bool setDeviceAccess(const std::string& zoneName,
+                     const std::string& device,
+                     bool grant,
+                     uint32_t flags);
+
+
+} // namespace lxc
+} // namespace vasum
+
+
+#endif // COMMON_LXC_CGROUP_HPP
index 621e3a2..db6130f 100644 (file)
@@ -180,6 +180,16 @@ void HostConnection::setUnlockZoneCallback(const UnlockZoneCallback& callback)
     mUnlockZoneCallback = callback;
 }
 
+void HostConnection::setGrantDeviceCallback(const GrantDeviceCallback& callback)
+{
+    mGrantDeviceCallback = callback;
+}
+
+void HostConnection::setRevokeDeviceCallback(const RevokeDeviceCallback& callback)
+{
+    mRevokeDeviceCallback = callback;
+}
+
 
 void HostConnection::onMessageCall(const std::string& objectPath,
                                    const std::string& interface,
@@ -316,6 +326,7 @@ void HostConnection::onMessageCall(const std::string& objectPath,
         if (mCreateZoneCallback){
             mCreateZoneCallback(id, result);
         }
+        return;
     }
 
     if (methodName == api::host::METHOD_DESTROY_ZONE) {
@@ -325,6 +336,7 @@ void HostConnection::onMessageCall(const std::string& objectPath,
         if (mDestroyZoneCallback){
             mDestroyZoneCallback(id, result);
         }
+        return;
     }
 
     if (methodName == api::host::METHOD_SHUTDOWN_ZONE) {
@@ -352,6 +364,7 @@ void HostConnection::onMessageCall(const std::string& objectPath,
         if (mLockZoneCallback){
             mLockZoneCallback(id, result);
         }
+        return;
     }
 
     if (methodName == api::host::METHOD_UNLOCK_ZONE) {
@@ -361,6 +374,30 @@ void HostConnection::onMessageCall(const std::string& objectPath,
         if (mUnlockZoneCallback){
             mUnlockZoneCallback(id, result);
         }
+        return;
+    }
+
+    if (methodName == api::host::METHOD_GRANT_DEVICE) {
+        const gchar* id = NULL;
+        const gchar* device = NULL;
+        uint32_t flags;
+        g_variant_get(parameters, "(&s&su)", &id, &device, &flags);
+
+        if (mGrantDeviceCallback){
+            mGrantDeviceCallback(id, device, flags, result);
+        }
+        return;
+    }
+
+    if (methodName == api::host::METHOD_REVOKE_DEVICE) {
+        const gchar* id = NULL;
+        const gchar* device = NULL;
+        g_variant_get(parameters, "(&s&s)", &id, &device);
+
+        if (mRevokeDeviceCallback){
+            mRevokeDeviceCallback(id, device, result);
+        }
+        return;
     }
 }
 
index 49e2a08..db8cf34 100644 (file)
@@ -101,6 +101,15 @@ public:
     typedef std::function<void(const std::string& id,
                                dbus::MethodResultBuilder::Pointer result
                               )> UnlockZoneCallback;
+    typedef std::function<void(const std::string& id,
+                               const std::string& device,
+                               uint32_t flags,
+                               dbus::MethodResultBuilder::Pointer result
+                              )> GrantDeviceCallback;
+    typedef std::function<void(const std::string& id,
+                               const std::string& device,
+                               dbus::MethodResultBuilder::Pointer result
+                              )> RevokeDeviceCallback;
 
     /**
      * Register proxy call callback
@@ -183,6 +192,16 @@ public:
     void setUnlockZoneCallback(const UnlockZoneCallback& callback);
 
     /**
+     * Register a callback called to grant device
+     */
+    void setGrantDeviceCallback(const GrantDeviceCallback& callback);
+
+    /**
+     * Register a callback called to revoke device
+     */
+    void setRevokeDeviceCallback(const RevokeDeviceCallback& callback);
+
+    /**
      * Make a proxy call
      */
     void proxyCallAsync(const std::string& busName,
@@ -213,6 +232,8 @@ private:
     StartZoneCallback mStartZoneCallback;
     LockZoneCallback mLockZoneCallback;
     UnlockZoneCallback mUnlockZoneCallback;
+    GrantDeviceCallback mGrantDeviceCallback;
+    RevokeDeviceCallback mRevokeDeviceCallback;
 
     void onNameAcquired();
     void onNameLost();
index 74ba96d..e17261c 100644 (file)
@@ -52,6 +52,8 @@ const std::string METHOD_SHUTDOWN_ZONE      = "ShutdownZone";
 const std::string METHOD_START_ZONE         = "StartZone";
 const std::string METHOD_LOCK_ZONE          = "LockZone";
 const std::string METHOD_UNLOCK_ZONE        = "UnlockZone";
+const std::string METHOD_GRANT_DEVICE       = "GrantDevice";
+const std::string METHOD_REVOKE_DEVICE      = "RevokeDevice";
 
 const std::string SIGNAL_ZONE_DBUS_STATE    = "ZoneDbusState";
 
@@ -122,6 +124,15 @@ const std::string DEFINITION =
     "    <method name='" + METHOD_UNLOCK_ZONE + "'>"
     "      <arg type='s' name='id' direction='in'/>"
     "    </method>"
+    "    <method name='" + METHOD_GRANT_DEVICE + "'>"
+    "      <arg type='s' name='id' direction='in'/>"
+    "      <arg type='s' name='device' direction='in'/>"
+    "      <arg type='u' name='flags' direction='in'/>"
+    "    </method>"
+    "    <method name='" + METHOD_REVOKE_DEVICE + "'>"
+    "      <arg type='s' name='id' direction='in'/>"
+    "      <arg type='s' name='device' direction='in'/>"
+    "    </method>"
     "    <signal name='" + SIGNAL_ZONE_DBUS_STATE + "'>"
     "      <arg type='s' name='zone'/>"
     "      <arg type='s' name='dbusAddress'/>"
index dcfa04c..ff6fb7e 100644 (file)
@@ -29,6 +29,7 @@
 #include "zone-dbus-definitions.hpp"
 #include "zones-manager.hpp"
 #include "zone-admin.hpp"
+#include "lxc/cgroup.hpp"
 #include "exception.hpp"
 
 #include "utils/paths.hpp"
@@ -132,6 +133,12 @@ ZonesManager::ZonesManager(const std::string& managerConfigPath)
     mHostConnection.setUnlockZoneCallback(bind(&ZonesManager::handleUnlockZoneCall,
                                                this, _1, _2));
 
+    mHostConnection.setGrantDeviceCallback(bind(&ZonesManager::handleGrantDeviceCall,
+                                                this, _1, _2, _3, _4));
+
+    mHostConnection.setRevokeDeviceCallback(bind(&ZonesManager::handleRevokeDeviceCall,
+                                                 this, _1, _2, _3));
+
     for (auto& zoneConfig : mConfig.zoneConfigs) {
         createZone(zoneConfig);
     }
@@ -1018,4 +1025,83 @@ void ZonesManager::handleUnlockZoneCall(const std::string& id,
     result->setVoid();
 }
 
+void ZonesManager::handleGrantDeviceCall(const std::string& id,
+                                         const std::string& device,
+                                         uint32_t flags,
+                                         dbus::MethodResultBuilder::Pointer result)
+{
+    LOGI("GrantDevice call; id=" << id << "; dev=" << device);
+
+    Lock lock(mMutex);
+
+    auto iter = mZones.find(id);
+    if (iter == mZones.end()) {
+        LOGE("Failed to grant device - no such zone id: " << id);
+        result->setError(api::ERROR_INVALID_ID, "No such zone id");
+        return;
+    }
+
+    auto& zone = *iter->second;
+    if (!zone.isRunning() && !zone.isPaused()) {
+        LOGE("Zone id=" << id << " is not running");
+        result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
+        return;
+    }
+
+    std::string devicePath = "/dev/" + device;
+
+    if (!lxc::isDevice(devicePath)) {
+        LOGE("Failed to grant device - cannot acces device: " << device);
+        result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
+        return;
+    }
+
+    // assume device node is created inside zone
+    if (!lxc::setDeviceAccess(id, devicePath, true, flags)) {
+        LOGE("Failed to grant device: " << device << " for zone: " << id);
+        result->setError(api::ERROR_INTERNAL, "Cannot grant device");
+        return;
+    }
+
+    result->setVoid();
+}
+
+void ZonesManager::handleRevokeDeviceCall(const std::string& id,
+                                          const std::string& device,
+                                          dbus::MethodResultBuilder::Pointer result)
+{
+    LOGI("RevokeDevice call; id=" << id << "; dev=" << device);
+
+    Lock lock(mMutex);
+
+    auto iter = mZones.find(id);
+    if (iter == mZones.end()) {
+        LOGE("Failed to revoke device - no such zone id: " << id);
+        result->setError(api::ERROR_INVALID_ID, "No such zone id");
+        return;
+    }
+
+    auto& zone = *iter->second;
+    if (!zone.isRunning() && !zone.isPaused()) {
+        LOGE("Zone id=" << id << " is not running");
+        result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
+        return;
+    }
+    std::string devicePath = "/dev/" + device;
+
+    if (!lxc::isDevice(devicePath)) {
+        LOGE("Failed to revoke device - cannot acces device: " << device);
+        result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
+        return;
+    }
+
+    if (!lxc::setDeviceAccess(id, devicePath, false, 0)) {
+        LOGE("Failed to revoke device: " << device << " for zone: " << id);
+        result->setError(api::ERROR_INTERNAL, "Cannot revoke device");
+        return;
+    }
+
+    result->setVoid();
+}
+
 } // namespace vasum
index ccf834a..a0a2695 100644 (file)
@@ -178,6 +178,13 @@ private:
                             dbus::MethodResultBuilder::Pointer result);
     void handleUnlockZoneCall(const std::string& id,
                               dbus::MethodResultBuilder::Pointer result);
+    void handleGrantDeviceCall(const std::string& id,
+                               const std::string& device,
+                               uint32_t flags,
+                               dbus::MethodResultBuilder::Pointer result);
+    void handleRevokeDeviceCall(const std::string& id,
+                                const std::string& device,
+                                dbus::MethodResultBuilder::Pointer result);
 };
 
 
index 180b5eb..9536ccb 100644 (file)
@@ -123,7 +123,22 @@ int getArrayStringLength(VsmArrayString astring, int max_len = -1)
 
 } // namespace
 
-BOOST_FIXTURE_TEST_SUITE(Client, Fixture)
+// make nice BOOST_*_EQUAL output
+// (does not work inside anonymous namespace)
+std::ostream& operator<<(std::ostream& out, VsmStatus status)
+{
+    switch(status) {
+    case VSMCLIENT_CUSTOM_ERROR: return out << "CUSTOM_ERROR";
+    case VSMCLIENT_IO_ERROR: return out << "IO_ERROR";
+    case VSMCLIENT_OPERATION_FAILED: return out << "OPERATION_FAILED";
+    case VSMCLIENT_INVALID_ARGUMENT: return out << "INVALID_ARGUMENT";
+    case VSMCLIENT_OTHER_ERROR: return out << "OTHER_ERROR";
+    case VSMCLIENT_SUCCESS: return out << "SUCCESS";
+    default: return out << "UNKNOWN(" << (int)status << ")";
+    };
+}
+
+BOOST_FIXTURE_TEST_SUITE(ClientSuite, Fixture)
 
 BOOST_AUTO_TEST_CASE(NotRunningServerTest)
 {
@@ -356,4 +371,26 @@ BOOST_AUTO_TEST_CASE(GetZoneIdByPidTest2)
     }
 }
 
+BOOST_AUTO_TEST_CASE(GrantRevokeTest)
+{
+    const std::string zoneId = "ut-zones-manager-console2-dbus";
+    const std::string dev = "tty3";
+
+    VsmClient client = vsm_client_create();
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client));
+
+    BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0));
+    BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_revoke_device(client, zoneId.c_str(), dev.c_str()));
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_lock_zone(client, zoneId.c_str()));
+    BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0));
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_unlock_zone(client, zoneId.c_str()));
+
+    BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_shutdown_zone(client, zoneId.c_str()));
+    BOOST_CHECK_EQUAL(VSMCLIENT_CUSTOM_ERROR, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0));
+    BOOST_CHECK_EQUAL(VSMCLIENT_CUSTOM_ERROR, vsm_revoke_device(client, zoneId.c_str(), dev.c_str()));
+
+    vsm_client_free(client);
+}
+
 BOOST_AUTO_TEST_SUITE_END()