summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Harpin <development@landsofshadow.co.uk>2015-07-07 07:21:19 +0100
committerAlex Harpin <development@landsofshadow.co.uk>2015-07-07 07:23:57 +0100
commit46639708dce06b6d779651d74e87ede4679f3780 (patch)
tree3324fd073ac427ee76584b8ead584a7e9022a20e
parent881fb397f3759dc356588becddcaff484d134e41 (diff)
downloadvyatta-cfg-46639708dce06b6d779651d74e87ede4679f3780.tar.gz
vyatta-cfg-46639708dce06b6d779651d74e87ede4679f3780.zip
vyatta-cfg: remove unused unionfs mounts following unclean config exits
If a configuration session is exited uncleanly, either as a result of a network disconnect / timeout or other forced quit from within config mode, the unionfs mount is left in place. These remain until the system is rebooted, giving an error when unmounting the config directory. This commit checks for old configuartion directories when a user enters config mode, removing any found that are owned by that user but not part of any active configuration seesion. Bug #564 http://bugzilla.vyos.net/show_bug.cgi?id=564
-rw-r--r--src/cstore/unionfs/cstore-unionfs.cpp133
-rw-r--r--src/cstore/unionfs/fspath.hpp2
2 files changed, 131 insertions, 4 deletions
diff --git a/src/cstore/unionfs/cstore-unionfs.cpp b/src/cstore/unionfs/cstore-unionfs.cpp
index 379f3bf..dccb626 100644
--- a/src/cstore/unionfs/cstore-unionfs.cpp
+++ b/src/cstore/unionfs/cstore-unionfs.cpp
@@ -25,6 +25,10 @@
#include <fcntl.h>
#include <sys/mount.h>
#include <wait.h>
+#include <dirent.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
#include <cli_cstore.h>
#include <cstore/unionfs/cstore-unionfs.hpp>
@@ -163,6 +167,42 @@ _unescape_path_name(const string& path)
return npath;
}
+vector<int> getActiveCommits()
+{
+ string process_name = "vbash";
+ vector<int> pids;
+
+ DIR *dp = opendir("/proc");
+ if (dp != NULL) {
+ struct dirent *dirp;
+ while ((dirp = readdir(dp))) {
+ int pid = atoi(dirp->d_name);
+ if (pid > 0) {
+ string command_path = string("/proc/") + dirp->d_name + "/cmdline";
+ ifstream command_file(command_path.c_str());
+ string command_line;
+ getline(command_file, command_line);
+ if (!command_line.empty()) {
+ size_t pos = command_line.find('\0');
+ if (pos != string::npos) {
+ command_line = command_line.substr(0, pos);
+ }
+ pos = command_line.rfind('/');
+ if (pos != string::npos) {
+ command_line = command_line.substr(pos + 1);
+ }
+ if (process_name == command_line) {
+ pids.push_back(pid);
+ }
+ }
+ }
+ }
+ }
+
+ closedir(dp);
+
+ return pids;
+}
////// constructor/destructor
/* "current session" constructor.
@@ -202,7 +242,6 @@ UnionfsCstore::UnionfsCstore(bool use_edit_level)
if ((val = getenv(C_ENV_CHANGE_ROOT.c_str()))) {
change_root = val;
}
-
/* note: the original perl API module does not use the edit levels
* from environment. only the actual CLI operations use them.
* so here make it an option.
@@ -342,6 +381,29 @@ UnionfsCstore::sessionChanged()
bool
UnionfsCstore::setupSession()
{
+ vector<FsPath> directories;
+ vector<int> pids;
+ vector<int> old_pids;
+ FsPath old_config;
+ FsPath work_base;
+
+ string work_string = work_root.path_cstr();
+ work_base = work_string.erase(work_string.find_last_of("/"));
+
+ try {
+ b_fs::directory_iterator di(work_base.path_cstr());
+ for (; di != b_fs::directory_iterator(); ++di) {
+ old_config = di->path().file_string().c_str();
+ if (path_is_directory(old_config)) {
+ directories.push_back(old_config);
+ }
+ }
+ } catch (...) {
+ if (path_exists(active_root)) {
+ output_internal("no session directories found [%s]\n", work_root.path_cstr());
+ }
+ }
+
if (!path_exists(work_root)) {
// session doesn't exist. create dirs.
try {
@@ -362,10 +424,75 @@ UnionfsCstore::setupSession()
return false;
}
} else if (!path_is_directory(work_root)) {
- output_internal("setup session not dir [%s]\n",
- work_root.path_cstr());
+ output_internal("setup session not dir [%s]\n", work_root.path_cstr());
return false;
}
+
+ pids = getActiveCommits();
+
+ struct stat config_info;
+ stat(work_root.path_cstr(), &config_info);
+ int current_uid = (int) config_info.st_uid;
+ bool failed = false;
+
+ for (size_t i = 0; i < directories.size(); i++) {
+ struct stat directory_info;
+ int directory_uid;
+ int current_pid = 0;
+
+ // find uid for the current directory and the active config directory
+
+ stat(directories[i].path_cstr(), &directory_info);
+ directory_uid = (int) directory_info.st_uid;
+
+ // remove old config session directories but only for the current user
+
+ if (directory_uid == current_uid) {
+ string config_match = work_base.path_cstr() + std::string("/new_config_");
+ string current_path = directories[i].path_cstr();
+
+ if (current_path.find(config_match) != std::string::npos) {
+ current_pid = atoi(current_path.erase(current_path.find(config_match), config_match.length()).c_str());
+
+ // umount only inactive config session directory, don't touch active sessions
+
+ if (std::find(pids.begin(), pids.end(), current_pid) == pids.end()) {
+ old_pids.push_back(current_pid);
+ output_internal("found inactive config [%d]\n", current_pid);
+ output_internal("umount [%s]\n", directories[i].path_cstr());
+ if (!do_umount(directories[i])) {
+ failed = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (!old_pids.empty()) {
+ for (size_t i = 0; i < directories.size(); i++) {
+
+ int current_pid;
+ string current_path;
+
+ current_path = directories[i].path_cstr();
+ current_pid = atoi(current_path.erase(0, (current_path.find_last_of("_")) + 1).c_str());
+
+ if (std::find(old_pids.begin(), old_pids.end(), current_pid) != old_pids.end()) {
+ try {
+ if (b_fs::remove_all(directories[i].path_cstr()) == 0) {
+ failed = true;
+ }
+ } catch (...) {
+ failed = true;
+ }
+ }
+ }
+ }
+
+ if (failed) {
+ output_internal("failed to remove old config session directories\n");
+ }
+
return true;
}
diff --git a/src/cstore/unionfs/fspath.hpp b/src/cstore/unionfs/fspath.hpp
index fbaafd6..fa15d0e 100644
--- a/src/cstore/unionfs/fspath.hpp
+++ b/src/cstore/unionfs/fspath.hpp
@@ -53,7 +53,7 @@ public:
FsPath& operator/=(const FsPath& p) {
_data /= p._data;
return *this;
- }
+ };
FsPath operator/(const FsPath& rhs) {
FsPath lhs(*this);
lhs /= rhs;