From 46639708dce06b6d779651d74e87ede4679f3780 Mon Sep 17 00:00:00 2001 From: Alex Harpin Date: Tue, 7 Jul 2015 07:21:19 +0100 Subject: 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 --- src/cstore/unionfs/cstore-unionfs.cpp | 133 +++++++++++++++++++++++++++++++++- src/cstore/unionfs/fspath.hpp | 2 +- 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 #include #include +#include + +#include +#include #include #include @@ -163,6 +167,42 @@ _unescape_path_name(const string& path) return npath; } +vector getActiveCommits() +{ + string process_name = "vbash"; + vector 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 directories; + vector pids; + vector 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; -- cgit v1.2.3