#include <functional>
#include <ostream>
#include <iostream>
+#include <fcntl.h>
using namespace std;
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
#define CLI_COMMAND_LINE_INTERFACE_HPP
#include <map>
+#include <vector>
#include <functional>
#include <ostream>
#include <string>
/**
* @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)
*/
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
"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"}}
+ }
}
};
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
/**
* @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
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)
* @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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
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,
if (mCreateZoneCallback){
mCreateZoneCallback(id, result);
}
+ return;
}
if (methodName == api::host::METHOD_DESTROY_ZONE) {
if (mDestroyZoneCallback){
mDestroyZoneCallback(id, result);
}
+ return;
}
if (methodName == api::host::METHOD_SHUTDOWN_ZONE) {
if (mLockZoneCallback){
mLockZoneCallback(id, result);
}
+ return;
}
if (methodName == api::host::METHOD_UNLOCK_ZONE) {
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;
}
}
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
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,
StartZoneCallback mStartZoneCallback;
LockZoneCallback mLockZoneCallback;
UnlockZoneCallback mUnlockZoneCallback;
+ GrantDeviceCallback mGrantDeviceCallback;
+ RevokeDeviceCallback mRevokeDeviceCallback;
void onNameAcquired();
void onNameLost();
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";
" <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'/>"
#include "zone-dbus-definitions.hpp"
#include "zones-manager.hpp"
#include "zone-admin.hpp"
+#include "lxc/cgroup.hpp"
#include "exception.hpp"
#include "utils/paths.hpp"
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);
}
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
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);
};
} // 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)
{
}
}
+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()