Skip to content

Commit

Permalink
captived: update mode resource for final symlink names and locations
Browse files Browse the repository at this point in the history
  • Loading branch information
glfejer authored and drbild committed Mar 20, 2019
1 parent 5d46c65 commit 1cc138e
Show file tree
Hide file tree
Showing 17 changed files with 174 additions and 29 deletions.
3 changes: 2 additions & 1 deletion captived/integration-tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ endforeach()

# Copy the test config directory
add_custom_command(TARGET integration-tests POST_BUILD
COMMAND cmake -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/config ${CURRENT_TEST_BINARY_DIR}/config
# using cp because copy_directory does not preserve symbolic links
COMMAND cp -P -R ${CMAKE_CURRENT_SOURCE_DIR}/config ${CURRENT_TEST_BINARY_DIR}/config
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
junk passthrough file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
junk secure-host file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
junk secure-lan file --- this isn't implemented yet.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
junk passthrough file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
junk secure-host file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
junk secure-lan file --- this isn't implemented yet.
23 changes: 13 additions & 10 deletions captived/integration-tests/mode_Test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ def setUp(self):
super(mode_Test, self).setUp()

def tearDown(self):
# put back to secure_host
resp = requests.put(URL, headers=HEADERS, json='secure_host')
# put back to secure-host
resp = requests.put(URL, headers=HEADERS, json='secure-host')
super(mode_Test, self).tearDown()

# This should get back the defult mode - which is secure_host
# This should get back the defult mode - which is secure-host
def test_01_get_mode(self):
resp = requests.get(URL)
self.assertEqual(resp.json(), "secure_host")
self.assertEqual(resp.json(), "secure-host")

def test_02_put_invalid_mode(self):
resp = requests.put(URL, headers=HEADERS, json='invalid-mode')
Expand All @@ -62,19 +62,22 @@ def test_04_put_invalid2(self):
self.assertEqual(resp.json(), orig_mode)

def test_05_put_secure_lan(self):
resp = requests.put(URL, headers=HEADERS, json='secure_lan')
resp = requests.put(URL, headers=HEADERS, json='secure-lan')
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.json(), 'secure_lan')
self.assertEqual(resp.json(), 'secure-lan')

def test_06_put_secure_host(self):
resp = requests.put(URL, headers=HEADERS, json='secure_host')
resp = requests.put(URL, headers=HEADERS, json='secure-host')
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.json(), 'secure_host')
self.assertEqual(resp.json(), 'secure-host')

def test_get_mode(self):
router_mode = os.path.join(DATA_PATH, 'data', 'default_target')
link_path = os.path.join(DATA_PATH, 'data', 'systemd', 'system', 'default.target')
real_path = os.path.realpath(link_path)
filename = os.path.basename(real_path)
router_mode = os.path.splitext(filename)[0]
resp = requests.get(URL)
self.assertMatchesFirstLineOfFile(router_mode, resp.json())
self.assertEqual(router_mode, resp.json())


if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions captived/integration-tests/root_level_status_Test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ def test_get_root(self):
('/rom/mac_address/1', 'mac_address'),
('/etc/mender/artifact_info', 'firmware_version'),
('/data/enftun/enf1/address', 'control_address'),
('/data/enftun/enf0/address', 'data_address'),
('/data/default_target', 'mode')
('/data/enftun/enf0/address', 'data_address')
]
for pair in check_these:
self.assertMatchesFirstLineOfFile(DATA_PATH + pair[0], jresp[pair[1]])
self.assertEqual('secure-host', jresp['mode'])



Expand Down
10 changes: 7 additions & 3 deletions captived/src/defines.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ const std::string FILE_WIFI_MAC_ADDRESS = "/rom/mac_address/1";
const std::string FILE_ENF_CONTROL_ADDRESS = "/data/enftun/enf1/address";
const std::string FILE_ENF_DATA_ADDRESS = "/data/enftun/enf0/address";
const std::string FILE_FIRMWARE_VERSION = "/etc/mender/artifact_info";
const std::string FILE_ROUTER_MODE = "/data/default_target";
const std::string FILE_WIFI_CONFIG_PASSTHROUGH =
"/data/connman/passthrough/wifi.config";
const std::string FILE_WIFI_CONFIG_SECURE_HOST =
"/data/connman/secure-host/wifi.config";
const std::string FILE_REBOOT_EXE = "/sbin/reboot";
const std::string LINK_MODE = "/data/systemd/system/default.target";
const std::string PATH_MODE_TARGET = "/lib/systemd/system";

const std::string URI_SERIAL_NUMBER = "/serial_number";
const std::string URI_WIFI_MAC_ADDRESS = "/mac_address";
Expand All @@ -35,9 +36,12 @@ const std::string URI_REBOOT = "/reboot";

// valid values for /mode URI
const std::string MODE_PASSTHROUGH = "passthrough";
const std::string MODE_SECURE_HOST = "secure_host";
const std::string MODE_SECURE_LAN = "secure_lan";
const std::string MODE_SECURE_HOST = "secure-host";
const std::string MODE_SECURE_LAN = "secure-lan";

extern const char* CONTENT_TYPE_JSON;

// define this so we don't use OS-specific values
const int ROUTER_CARD_PATH_MAX = 1024;

} // namespace captived
2 changes: 1 addition & 1 deletion captived/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ int main(int argc, char* argv[]) {
rest::line_resource firmware_version(URI_FIRMWARE_VERSION, sys,
FILE_FIRMWARE_VERSION, false);

rest::mode router_mode(URI_ROUTER_MODE, sys, FILE_ROUTER_MODE);
rest::mode router_mode(URI_ROUTER_MODE, sys, PATH_MODE_TARGET, LINK_MODE);

rest::wifi_config wifi_config_passthrough(URI_WIFI_CONFIG_PASSTHROUGH, sys,
FILE_WIFI_CONFIG_PASSTHROUGH);
Expand Down
76 changes: 69 additions & 7 deletions captived/src/rest/mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,39 @@ bool is_valid_mode(std::string mode) {

namespace rest {

mode::mode(std::string path, system& system, std::string filename)
: line_resource(path, system, filename, true) {}
mode::mode(std::string path, system& system, std::string target_path,
std::string mode_symlink)
: resource(path), system_(system), mode_symlink_(mode_symlink),
target_path_(target_path) {}

bool mode::line(std::string new_line) {
return is_valid_mode(new_line) && line_resource::line(new_line);
/**
* get
* Handles the get operation on the /mode REST resource.
*/
mode::resp_type mode::get(resource::req_type) {
auto temp_mode = router_mode();
if (!temp_mode) {
auto msg = "Error: failed to find router mode.";
return internal_server_error(json::string(msg));
}

return ok(json::string(*temp_mode));
}

mode::resp_type mode::put(mode::req_type body) {
/**
* put
* Handles the 'put' operation on the /mode REST resource
*/
mode::resp_type mode::put(resource::req_type body) {
// Parse and validate the request body
json_t* root = body.get();
std::string old_mode_str;

// get the mode before we change it.
auto old_mode = router_mode();
if (old_mode) {
old_mode_str = *old_mode;
}

if (!json_is_string(root)) {
auto msg = "Error: request body is not a JSON string.";
Expand All @@ -32,11 +55,50 @@ mode::resp_type mode::put(mode::req_type body) {
std::string newval = json_string_value(root);

if (!is_valid_mode(newval)) {
auto msg = "Error: mode must is invalid.";
auto msg = "Error: mode is invalid.";
return bad_request(json::string(msg));
}

return line_resource::put(body);
if (newval != old_mode_str) {
// only need to update the resource if we have a new value
if (!router_mode(newval)) {
auto msg = "Error: failed to update resource.";
return internal_server_error(json::string(msg));
}
}
return get(json::null());
}

/**
* Returns the configured mode of the router card.
* Will be one of "passthrough", "secure-host", or "secure-lan".
*
* @returns The router card mode or None on an error.
*/
std::experimental::optional<std::string> mode::router_mode() {
auto fq_link = system_.symlink_target(mode_symlink_);
if (!fq_link) {
return {};
}

size_t period = fq_link->find_last_of('.');
size_t slash = fq_link->find_last_of('/');
std::string mode = fq_link->substr(slash + 1, period - slash - 1);
return {mode};
}

/**
* Sets the router card's configured mode.
*
* The change will be stored, but not take effect until a reboot.
*
* @param new_mode The mode to set. Valid input modes are: "passthrough",
* "secure-host", or "secure-lan".
* @returns true on success, false otherwise.
*/
bool mode::router_mode(std::string new_mode) {
std::string target_name = target_path_ + "/" + new_mode + ".target";
return system_.symlink_target(target_name, mode_symlink_);
}

} // namespace rest
Expand Down
28 changes: 24 additions & 4 deletions captived/src/rest/mode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,33 @@ namespace rest {
/**
* A resource for the router card mode.
*/
class mode : public line_resource {
class mode : public resource {
public:
mode(std::string path, system& system, std::string filename);

bool line(std::string new_line) override;
mode(std::string path, system& system, std::string target_path,
std::string mode_symlink);
~mode() override = default;

resp_type get(req_type) override;
resp_type put(req_type body) override;

/**
* Fetch the mode.
*
* @returns The mode or None on an error.
*/
virtual std::experimental::optional<std::string> router_mode();

/**
* Set the mode.
*
* @returns true on success and false on an error
*/
virtual bool router_mode(std::string new_mode);

protected:
system& system_;
std::string mode_symlink_; // fully qualified path to symlink we modify
std::string target_path_; // path to the systemd file where we point.
};

} // namespace rest
Expand Down
48 changes: 48 additions & 0 deletions captived/src/system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
#include <iostream>
#include <sstream>
#include <string>
#include <unistd.h>

#include <experimental/optional>
#include <sys/stat.h>

namespace captived {

Expand Down Expand Up @@ -92,6 +94,52 @@ class system {
return write(filename, line + '\n');
}

/**
* Return the target of a symbolic link.
*
* Note: Will only follow a single link and assumes argument IS a link
*/
std::experimental::optional<std::string>
symlink_target(std::string link_path) {
char buf[ROUTER_CARD_PATH_MAX];
std::string full_link_path = chroot_ + link_path;

ssize_t nbytes =
readlink(full_link_path.c_str(), buf, ROUTER_CARD_PATH_MAX);
if ((nbytes < 0) || (nbytes >= ROUTER_CARD_PATH_MAX)) { // error case
return {};
}

std::string target{buf, static_cast<size_t>(nbytes)};
return {target};
}

/**
* Add or change the symlink to a target.
*
* If a file already exists and is a symlink, it is deleted before a
* new link is created. If the file is not a link, error out.
*/
bool symlink_target(std::string target, std::string link) {
std::string fq_target = chroot_ + target;
std::string fq_link = chroot_ + link;

// check if file exists
struct stat buffer;
int stat_result = lstat(fq_link.c_str(), &buffer);
if (stat_result == 0) {
if (S_ISLNK(buffer.st_mode)) { // delete if a link
remove(fq_link.c_str());
} else { // error if other type of file
return false;
}
}

// create new symbolic link
int result = symlink(fq_target.c_str(), fq_link.c_str());
return (result == 0);
}

private:
std::string chroot_;
};
Expand Down

0 comments on commit 1cc138e

Please sign in to comment.