This is an automated email from the ASF dual-hosted git repository.
dragon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 944e51d Runroot: Update verify command and refactoring
944e51d is described below
commit 944e51dda358b04b5ad2beb9daa159518cae5e11
Author: Xavier Chi <[email protected]>
AuthorDate: Fri Nov 30 11:30:59 2018 -0600
Runroot: Update verify command and refactoring
---
doc/appendices/command-line/traffic_layout.en.rst | 9 +-
src/traffic_layout/engine.cc | 589 +++++++++++-----------
src/traffic_layout/engine.h | 18 +-
src/traffic_layout/traffic_layout.cc | 2 +-
src/tscore/ArgParser.cc | 7 +-
tests/gold_tests/runroot/runroot_init.test.py | 1 -
tests/gold_tests/runroot/runroot_verify.test.py | 15 +-
7 files changed, 337 insertions(+), 304 deletions(-)
diff --git a/doc/appendices/command-line/traffic_layout.en.rst
b/doc/appendices/command-line/traffic_layout.en.rst
index 3d4fedd..0898443 100644
--- a/doc/appendices/command-line/traffic_layout.en.rst
+++ b/doc/appendices/command-line/traffic_layout.en.rst
@@ -54,7 +54,7 @@ First we need to create a runroot. It can be created simply
by calling command `
A runroot will be created in ``/path/to/runroot``, available for other
programs to use.
If the path is not specified, the current working directory will be used.
-To run traffic_manager, for example, using the runroot, there are several ways:
+For example, to run traffic_manager, using the runroot, there are several ways:
#. ``/path/to/runroot/bin/traffic_manager``
#. ``traffic_manager --run-root=/path/to/runroot``
#. ``traffic_manager --run-root=/path/to/runroot/runroot.yaml``
@@ -127,6 +127,13 @@ Example: ::
traffic_layout verify (--path /path/to/sandbox/) (--fix) (--with-user root)
+.. Warning::
+
+ If a custom layout is used and system files are included in some
directories, ``--fix`` option might potentially have unexpected behaviors.
+ For example, if sysconfdir is defined as ``/etc`` instead of
``/etc/trafficserver`` in ``runroot.yaml``,
+ ``--fix`` may perform permission changes on the system configuration files.
With normally created runroot with default layout,
+ there is no such issue since traffic server related files are filtered.
+
=======
Options
=======
diff --git a/src/traffic_layout/engine.cc b/src/traffic_layout/engine.cc
index ea2c96b..e6a2703 100644
--- a/src/traffic_layout/engine.cc
+++ b/src/traffic_layout/engine.cc
@@ -39,16 +39,20 @@
#include <ftw.h>
#include <yaml-cpp/yaml.h>
#include <grp.h>
+#include <sysexits.h>
static const long MAX_LOGIN = ink_login_name_max();
static constexpr int MAX_GROUP_NUM = 32;
-// for nftw check_directory
-std::string directory_check;
+// Personally I don't really like the way that nftw needs global variables.
Right now there is no other options.
+// This iteration through directory can be avoided after std::filesystem is in.
+// for nftw check_directory
+static std::string empty_check_directory;
// for 'verify --fix' nftw iteration
-int permission;
-std::string cur_fix_dir;
+static PermissionEntry permission_entry;
+// if fix_flag is set, permission handler will perform fixing operation
+static bool fix_flag = false;
// the function for the checking of the yaml file in the passed in path
// if found return the path, if not return empty string
@@ -85,51 +89,27 @@ check_parent_path(const std::string &path)
return {};
}
-// check if we can create the runroot using path
-// return true if the path is good to use
-static bool
-check_run_path(const std::string &arg, const bool forceflag)
-{
- if (arg.empty()) {
- return false;
- }
- // the condition of force create
- if (is_directory(arg) && forceflag) {
- std::cout << "Forcing creating runroot ..." << std::endl;
- return true;
- }
-
- // if directory already exist
- if (is_directory(arg)) {
- return true;
- } else {
- // try to create & remove
- if (!create_directory(arg)) {
- return false;
- }
- remove_directory(arg);
- return true;
- }
-}
-
// handle the path of the engine during parsing
static std::string
path_handler(const std::string &path, bool run_flag, const std::string
&command)
{
+ std::string cur_working_dir = "";
char cwd[PATH_MAX];
- if (getcwd(cwd, sizeof(cwd)) == nullptr) {
- ink_fatal("unexcepted failure from getcwd() code=%d", errno);
+ if (!getcwd(cwd, sizeof(cwd))) {
+ ink_warning("unexcepted failure from getcwd() - %s", strerror(errno));
+ } else {
+ cur_working_dir = cwd;
}
if (run_flag) {
// no path passed in, use cwd
if (path.empty()) {
- return cwd;
+ return cur_working_dir;
}
if (path[0] == '/') {
return path;
} else {
- return Layout::relative_to(cwd, path);
+ return Layout::relative_to(cur_working_dir, path);
}
}
@@ -140,7 +120,7 @@ path_handler(const std::string &path, bool run_flag, const
std::string &command)
if (path[0] == '/') {
cmdpath = check_path(path);
} else {
- cmdpath = check_path(Layout::relative_to(cwd, path));
+ cmdpath = check_path(Layout::relative_to(cur_working_dir, path));
}
if (!cmdpath.empty()) {
return cmdpath;
@@ -148,7 +128,7 @@ path_handler(const std::string &path, bool run_flag, const
std::string &command)
}
// 2. find cwd or parent path of cwd to check
- std::string cwdpath = check_parent_path(cwd);
+ std::string cwdpath = check_parent_path(cur_working_dir);
if (!cwdpath.empty()) {
return cwdpath;
}
@@ -157,8 +137,9 @@ path_handler(const std::string &path, bool run_flag, const
std::string &command)
char RealBinPath[PATH_MAX] = {0};
if (!command.empty() && realpath(command.c_str(), RealBinPath) != nullptr) {
std::string bindir = RealBinPath;
- bindir = bindir.substr(0, bindir.find_last_of("/")); //
getting the bin dir not executable path
- bindir = check_parent_path(bindir);
+
+ bindir = bindir.substr(0, bindir.find_last_of("/")); // getting the bin
dir not executable path
+ bindir = check_parent_path(bindir);
if (!bindir.empty()) {
return bindir;
}
@@ -167,29 +148,11 @@ path_handler(const std::string &path, bool run_flag,
const std::string &command)
return path;
}
-// if directory is not empty, ask input from user Y/N
+// check if there is any directory inside empty_check_directory to see if it
is empty or not
static int
-check_directory(const char *path, const struct stat *s, int flag, struct FTW
*f)
+check_directory_empty(const char *path, const struct stat *s, int flag)
{
- std::string checkpath = path;
- if (checkpath != directory_check) {
- // check for valid Y/N
- for (int i = 0; i < 3; i++) {
- std::cout << "Are you sure to create runroot inside a non-empty
directory Y/N: ";
- std::string input;
- std::cin >> input;
- if (input == "Y" || input == "y") {
- // terminate nftw
- return 1;
- }
- if (input == "N" || input == "n") {
- exit(0);
- }
- }
- ink_error("Invalid input Y/N");
- exit(70);
- }
- return 0;
+ return std::string(path) != empty_check_directory ? -1 : 0;
}
void
@@ -208,14 +171,20 @@ void
LayoutEngine::create_runroot()
{
// set up options
- std::string path = path_handler(arguments.get("path").value(), true,
_argv[0]);
+ std::string path = path_handler(arguments.get("path").value(), true,
_argv[0]);
+ if (path.empty()) {
+ ink_error("Path not valid for creating");
+ status_code = EX_SOFTWARE;
+ return;
+ }
bool force_flag = arguments.get("force");
bool abs_flag = arguments.get("absolute");
std::string layout_file = arguments.get("layout").value();
if (layout_file.find("runroot.yaml") != std::string::npos) {
ink_error(
"'runroot.yaml' is a potentially dangerous name for '--layout'
option.\nPlease set other name to the file for '--layout'");
- exit(70);
+ status_code = EX_SOFTWARE;
+ return;
}
// deal with the copy style
CopyStyle copy_style = HARD;
@@ -230,13 +199,10 @@ LayoutEngine::create_runroot()
copy_style = HARD;
} else {
ink_error("Unknown copy style: '%s'", style.c_str());
- exit(70);
+ status_code = EX_USAGE;
+ return;
}
}
- // check validity of the path
- if (!check_run_path(path, force_flag)) {
- ink_fatal("Failed to create runroot with path '%s'", path.c_str());
- }
std::string original_root = Layout::get()->prefix;
std::string ts_runroot = path;
// check for existing runroot to use rather than create new one
@@ -247,15 +213,35 @@ LayoutEngine::create_runroot()
return;
}
if (!force_flag && !check_parent_path(ts_runroot).empty()) {
- ink_fatal("Cannot create runroot inside another runroot");
+ ink_error("Cannot create runroot inside another runroot");
+ status_code = EX_SOFTWARE;
+ return;
}
std::cout << "creating runroot - " + ts_runroot << std::endl;
// check if directory is empty when --force is not used
if (is_directory(ts_runroot) && !force_flag) {
- directory_check = ts_runroot;
- nftw(ts_runroot.c_str(), check_directory, OPEN_MAX_FILE, FTW_DEPTH);
+ empty_check_directory = ts_runroot;
+ if (ftw(ts_runroot.c_str(), check_directory_empty, OPEN_MAX_FILE) != 0) {
+ // if the directory is not empty, check for valid Y/N
+ for (int i = 0; i < 3; i++) {
+ std::cout << "Are you sure to create runroot inside a non-empty
directory Y/N: ";
+ std::string input;
+ std::cin >> input;
+ if (input == "Y" || input == "y") {
+ break;
+ }
+ if (input == "N" || input == "n") {
+ return;
+ }
+ if (i == 2) {
+ ink_error("Invalid input Y/N");
+ status_code = EX_SOFTWARE;
+ return;
+ }
+ }
+ }
}
// create new root & copy from original to new runroot. then fill in the map
@@ -281,10 +267,10 @@ LayoutEngine::create_runroot()
if (layout_file.size() != 0) {
try {
YAML::Node yamlfile = YAML::LoadFile(layout_file);
- for (auto &&it : yamlfile) {
+ for (const auto &it : yamlfile) {
std::string key = it.first.as<std::string>();
std::string value = it.second.as<std::string>();
- auto &&iter = new_map.find(key);
+ auto iter = new_map.find(key);
if (iter != new_map.end()) {
iter->second = value;
} else {
@@ -303,7 +289,7 @@ LayoutEngine::create_runroot()
// symlink the executables
// set up path_map for yaml to emit key-value pairs
RunrootMapType path_map;
- for (auto &&it : original_map) {
+ for (const auto &it : original_map) {
// path handle
std::string join_path;
if (it.second[0] && it.second[0] == '/') {
@@ -345,7 +331,7 @@ LayoutEngine::create_runroot()
YAML::Emitter yamlfile;
// emit key value pairs to the yaml file
yamlfile << YAML::BeginMap;
- for (auto &&it : dir_vector) {
+ for (const auto &it : dir_vector) {
yamlfile << YAML::Key << it;
yamlfile << YAML::Value << path_map[it];
}
@@ -367,18 +353,14 @@ LayoutEngine::remove_runroot()
std::string path = path_handler(arguments.get("path").value(), false,
_argv[0]);
// check validity of the path
if (path.empty()) {
- ink_fatal("Path not valid (runroot.yaml not found)");
+ ink_error("Path not valid (runroot.yaml not found)");
+ status_code = EX_IOERR;
+ return;
}
std::string clean_root = path;
append_slash(clean_root);
- char cwd[PATH_MAX];
- if (getcwd(cwd, sizeof(cwd)) == nullptr) {
- ink_fatal("unexcepted failure from getcwd() code=%d", errno);
- }
- std::string cur_working_dir = cwd;
-
if (arguments.get("force")) {
// the force remove
std::cout << "Forcing removing runroot ..." << std::endl;
@@ -390,11 +372,20 @@ LayoutEngine::remove_runroot()
RunrootMapType map = runroot_map(Layout::relative_to(clean_root,
"runroot.yaml"));
map.erase(LAYOUT_PREFIX);
map.erase(LAYOUT_EXEC_PREFIX);
- for (auto &&it : map) {
+ // get current working directory
+ std::string cur_working_dir = "";
+ char cwd[PATH_MAX];
+ if (getcwd(cwd, sizeof(cwd)) == nullptr) {
+ ink_warning("unexcepted failure from getcwd() - %s", strerror(errno));
+ } else {
+ cur_working_dir = cwd;
+ }
+ for (const auto &it : map) {
std::string dir = it.second;
append_slash(dir);
// get the directory to remove: prefix/etc/trafficserver -> prefix/etc
dir = dir.substr(0, dir.substr(clean_root.size()).find_first_of("/") +
clean_root.size());
+ // don't remove cwd
if (cur_working_dir != dir) {
remove_directory(dir);
} else {
@@ -418,230 +409,250 @@ LayoutEngine::remove_runroot()
}
}
-// return an array containing gid and uid of provided user
-static std::pair<gid_t, uid_t>
-get_giduid(std::string &user)
-{
- passwd *pwd;
- if (user[0] == '#') {
- // Numeric user notation.
- uid_t uid = (uid_t)atoi(&user[1]);
- pwd = getpwuid(uid);
- } else {
- pwd = getpwnam(user.c_str());
- }
- if (!pwd) {
- // null ptr
- ink_fatal("missing password database entry for '%s'", user.c_str());
- }
- user = pwd->pw_name;
- std::pair<gid_t, uid_t> giduid = {pwd->pw_gid, pwd->pw_uid};
- return giduid;
-}
-
-// output read permission
-static void
-output_read_permission(const std::string &permission)
+// check permission for verify
+static int
+permission_handler(const char *path, const struct stat *s, int flag)
{
- if (permission[0] == '1') {
- std::cout << "\tRead Permission: \033[1;32mPASSED\033[0m" << std::endl;
- } else {
- std::cout << "\tRead Permission: \033[1;31mFAILED\033[0m" << std::endl;
+ std::string cur_directory = permission_entry.name;
+ // filter traffic server related files
+ if (!filter_ts_files(cur_directory, path)) {
+ return 0;
}
-}
-
-// output write permission
-static void
-output_write_permission(const std::string &permission)
-{
- if (permission[1] == '1') {
- std::cout << "\tWrite Permission: \033[1;32mPASSED\033[0m" << std::endl;
- } else {
- std::cout << "\tWrite Permission: \033[1;31mFAILED\033[0m" << std::endl;
+ if (flag == FTW_NS || !s) {
+ ink_warning("unable to stat() destination path %s - %s", path,
strerror(errno));
+ return -1;
}
-}
-// output execute permission
-static void
-output_execute_permission(const std::string &permission)
-{
- if (permission[2] == '1') {
- std::cout << "\tExecute Permission: \033[1;32mPASSED\033[0m" << std::endl;
+ int ret = 0;
+ // --------- for directories ---------
+ if (flag == FTW_D || flag == FTW_DNR) {
+ // always need read permission for directories
+ if (!(s->st_mode & permission_entry.r_mode)) {
+ if (fix_flag) {
+ if (chmod(path, s->st_mode | permission_entry.r_mode) < 0) {
+ ink_warning("Unable to change file mode on %s - %s", path,
strerror(errno));
+ } else {
+ std::cout << "Fixed read permission: " << path << std::endl;
+ }
+ } else {
+ std::cout << "Read permission failed for directory: " << path <<
std::endl;
+ ret = -1;
+ }
+ }
+ // need write permission for logdir, runtimedir and cachedir
+ if (cur_directory == LAYOUT_LOGDIR || cur_directory == LAYOUT_RUNTIMEDIR
|| cur_directory == LAYOUT_CACHEDIR) {
+ if (!(s->st_mode & permission_entry.w_mode)) {
+ if (fix_flag) {
+ if (chmod(path, s->st_mode | permission_entry.w_mode) < 0) {
+ ink_warning("Unable to change file mode on %s - %s", path,
strerror(errno));
+ } else {
+ std::cout << "Fixed write permission: " << path << std::endl;
+ }
+ } else {
+ std::cout << "Write permission failed for directory: " << path <<
std::endl;
+ ret = -1;
+ }
+ }
+ }
+ // always need execute permission for directories
+ if (!(s->st_mode & permission_entry.e_mode)) {
+ if (fix_flag) {
+ if (chmod(path, s->st_mode | permission_entry.e_mode) < 0) {
+ ink_warning("Unable to change file mode on %s - %s", path,
strerror(errno));
+ } else {
+ std::cout << "Fixed execute permission: " << path << std::endl;
+ }
+ } else {
+ std::cout << "Execute permission failed for directory: " << path <<
std::endl;
+ ret = -1;
+ }
+ }
} else {
- std::cout << "\tExecute Permission: \033[1;31mFAILED\033[0m" << std::endl;
+ // --------- for files ---------
+ // always need read permission for all files
+ if (!(s->st_mode & permission_entry.r_mode)) {
+ if (fix_flag) {
+ if (chmod(path, s->st_mode | permission_entry.r_mode) < 0) {
+ ink_warning("Unable to change file mode on %s - %s", path,
strerror(errno));
+ } else {
+ std::cout << "Fixed read permission: " << path << std::endl;
+ }
+ } else {
+ std::cout << "Read permission failed for file " << path << std::endl;
+ ret = -1;
+ }
+ }
+ // need write permission for files in logdir, runtimedir and cachedir
+ if (cur_directory == LAYOUT_LOGDIR || cur_directory == LAYOUT_RUNTIMEDIR
|| cur_directory == LAYOUT_CACHEDIR) {
+ if (!(s->st_mode & permission_entry.w_mode)) {
+ if (fix_flag) {
+ if (chmod(path, s->st_mode | permission_entry.w_mode) < 0) {
+ ink_warning("Unable to change file mode on %s - %s", path,
strerror(errno));
+ } else {
+ std::cout << "Fixed write permission: " << path << std::endl;
+ }
+ } else {
+ std::cout << "Write permission failed for file " << path <<
std::endl;
+ ret = -1;
+ }
+ }
+ }
+ // execute permission on files in bindir, sbindir, libdir and libexecdir
+ if (cur_directory == LAYOUT_BINDIR || cur_directory == LAYOUT_SBINDIR ||
cur_directory == LAYOUT_LIBDIR ||
+ cur_directory == LAYOUT_LIBEXECDIR) {
+ std::string tmp_path = path;
+ // skip the files in perl5 and pkgconfig from libdir
+ if (tmp_path.find("/perl5/") != std::string::npos ||
tmp_path.find("/pkgconfig/") != std::string::npos) {
+ return 0;
+ }
+ if (!(s->st_mode & permission_entry.e_mode)) {
+ if (fix_flag) {
+ if (chmod(path, s->st_mode | permission_entry.e_mode) < 0) {
+ ink_warning("Unable to change file mode on %s - %s", path,
strerror(errno));
+ } else {
+ std::cout << "Fixed execute permission: " << path << std::endl;
+ }
+ } else {
+ std::cout << "Execute permission failed for file: " << path <<
std::endl;
+ ret = -1;
+ }
+ }
+ }
}
+ return ret;
}
-// chmod the file permission
-static int
-chmod_files_permission(const char *path, const struct stat *s, int flag,
struct FTW *f)
+// used for prefix, exec_prefix and localstatedir
+// only check the read/execute permission on those directories
+static bool
+check_directory_permission(const char *path)
{
- // ----- filter traffic server related files -----
- if (!filter_ts_files(cur_fix_dir, path)) {
- return 0;
- }
struct stat stat_buffer;
if (stat(path, &stat_buffer) < 0) {
- ink_warning("unable to stat() destination path %s: %s", path,
strerror(errno));
- return 0;
+ ink_warning("unable to stat() destination path %s - %s", path,
strerror(errno));
+ return false;
}
- if (chmod(path, stat_buffer.st_mode | permission) < 0) {
- ink_warning("Unable to change file mode on %s: %s", path, strerror(errno));
+ if (!(stat_buffer.st_mode & permission_entry.r_mode)) {
+ std::cout << "Read permission failed for: " << path << std::endl;
+ return false;
}
- return 0;
-}
-
-// the fixing permission of runroot used by verify command
-static void
-fix_runroot(RunrootMapType &path_map, RunrootMapType &permission_map,
RunrootMapType &usergroup_map)
-{
- if (getuid() != 0) {
- ink_error("To fix permission issues, root privileges are required.\nPlease
run with sudo.");
- exit(70);
- }
- for (auto &&it : permission_map) {
- std::string name = it.first;
- std::string usergroup = usergroup_map[name];
- std::string path = path_map[name];
- std::cout << "Fixing " + name + "..." << std::endl;
-
- int read_permission;
- int write_permission;
- int execute_permission;
- if (usergroup == "owner") {
- read_permission = S_IRUSR;
- write_permission = S_IWUSR;
- execute_permission = S_IXUSR;
- } else if (usergroup == "group") {
- read_permission = S_IRGRP;
- write_permission = S_IWGRP;
- execute_permission = S_IXGRP;
- } else {
- read_permission = S_IROTH;
- write_permission = S_IWOTH;
- execute_permission = S_IXOTH;
- }
- // fix read permission
- permission = read_permission;
- cur_fix_dir = name;
- nftw(path.c_str(), chmod_files_permission, OPEN_MAX_FILE, FTW_DEPTH);
- // fix write permission
- if (name == LAYOUT_LOGDIR || name == LAYOUT_RUNTIMEDIR || name ==
LAYOUT_CACHEDIR) {
- permission = write_permission;
- nftw(path.c_str(), chmod_files_permission, OPEN_MAX_FILE, FTW_DEPTH);
- }
- // fix execute permission
- if (name == LAYOUT_BINDIR || name == LAYOUT_SBINDIR || name ==
LAYOUT_LIBDIR || name == LAYOUT_LIBEXECDIR) {
- permission = execute_permission;
- nftw(path.c_str(), chmod_files_permission, OPEN_MAX_FILE, FTW_DEPTH);
- }
+ if (!(stat_buffer.st_mode & permission_entry.e_mode)) {
+ std::cout << "Execute permission failed for: " << path << std::endl;
+ return false;
}
+ return true;
}
+#if defined(darwin)
+// on Darwin, getgrouplist() takes int.
+using gid_type = int;
+#else
+using gid_type = gid_t;
+#endif
+
// helper function to check if user is from the same group of path_gid
static bool
-from_group(std::string const &user, gid_t group_id, gid_t path_gid)
+from_group(const char *user, gid_type group_id, gid_type path_gid)
{
int ngroups = MAX_GROUP_NUM;
- // Retrieve group list
-#if defined(darwin)
- // on Darwin, getgrouplist() takes int.
- int groups[ngroups];
- if (getgrouplist(user.c_str(), static_cast<int>(group_id), groups, &ngroups)
== -1) {
- ink_fatal("Failed to get group list as user '%s'\n", user.c_str());
- }
- for (int i = 0; i < ngroups; i++) {
- if (static_cast<int>(path_gid) == groups[i]) {
- return true;
- }
- }
-#else
- gid_t groups[ngroups];
- if (getgrouplist(user.c_str(), group_id, groups, &ngroups) == -1) {
- ink_fatal("Failed to get group list as user '%s'\n", user.c_str());
+ gid_type groups[ngroups];
+ if (getgrouplist(user, group_id, groups, &ngroups) == -1) {
+ ink_warning("Unable to get group list as user '%s'\n", user);
+ return false;
}
for (int i = 0; i < ngroups; i++) {
if (path_gid == groups[i]) {
return true;
}
}
-#endif
return false;
}
// set permission to the map in verify runroot
static void
-set_permission(std::vector<std::string> const &dir_vector, RunrootMapType
&path_map, RunrootMapType &permission_map,
- RunrootMapType &usergroup_map, std::string &user)
+set_permission(PermissionMapType &permission_map, const struct passwd *pwd)
{
- // active group and user of the path
- std::pair<gid_t, uid_t> giduid = get_giduid(user);
-
- gid_t ts_gid = giduid.first;
- uid_t ts_uid = giduid.second;
-
- struct stat stat_buffer;
+ gid_t ts_gid = pwd->pw_gid;
+ uid_t ts_uid = pwd->pw_uid;
+ bool new_line = false;
// set up permission map for all permissions
- for (auto &&it : dir_vector) {
- std::string name = it;
- std::string value = path_map[name];
-
- if (name == LAYOUT_PREFIX || name == LAYOUT_EXEC_PREFIX) {
- continue;
- }
+ for (auto &it : permission_map) {
+ std::string name = it.first;
+ std::string value = it.second.path;
+ struct stat stat_buffer;
if (stat(value.c_str(), &stat_buffer) < 0) {
- ink_warning("unable to stat() destination path %s: %s", value.c_str(),
strerror(errno));
+ ink_warning("unable to stat() destination path %s - %s", value.c_str(),
strerror(errno));
+ it.second.result = false;
+ new_line = true;
continue;
}
gid_t path_gid = stat_buffer.st_gid;
uid_t path_uid = stat_buffer.st_uid;
- permission_map[name] = "000"; // default rwx all 0
if (ts_uid == path_uid) {
- // check for owner permission of st_mode
- usergroup_map[name] = "owner";
- if (stat_buffer.st_mode & S_IRUSR) {
- permission_map[name][0] = '1';
- }
- if (stat_buffer.st_mode & S_IWUSR) {
- permission_map[name][1] = '1';
- }
- if (stat_buffer.st_mode & S_IXUSR) {
- permission_map[name][2] = '1';
- }
- } else if (from_group(user, ts_gid, path_gid)) { // current user is in the
path_gid group
- // check for group permission of st_mode
- usergroup_map[name] = "group";
- if (stat_buffer.st_mode & S_IRGRP) {
- permission_map[name][0] = '1';
- }
- if (stat_buffer.st_mode & S_IWGRP) {
- permission_map[name][1] = '1';
- }
- if (stat_buffer.st_mode & S_IXGRP) {
- permission_map[name][2] = '1';
- }
+ it.second.r_mode = S_IRUSR;
+ it.second.w_mode = S_IWUSR;
+ it.second.e_mode = S_IXUSR;
+ } else if (from_group(pwd->pw_name, ts_gid, path_gid)) {
+ it.second.r_mode = S_IRGRP;
+ it.second.w_mode = S_IWGRP;
+ it.second.e_mode = S_IXGRP;
} else {
- // check for others permission of st_mode
- usergroup_map[name] = "others";
- if (stat_buffer.st_mode & S_IROTH) {
- permission_map[name][0] = '1';
- }
- if (stat_buffer.st_mode & S_IWOTH) {
- permission_map[name][1] = '1';
+ it.second.r_mode = S_IROTH;
+ it.second.w_mode = S_IWOTH;
+ it.second.e_mode = S_IXOTH;
+ }
+ // set the result in permission entry
+ permission_entry = it.second;
+ it.second.result = true;
+ // Special case with prefix, exec_prefix and localstatedir. We only need
to check the directory itself
+ // but not the files within because they are just container dir for others.
+ if (name == LAYOUT_PREFIX || name == LAYOUT_EXEC_PREFIX || name ==
LAYOUT_LOCALSTATEDIR) {
+ if (!check_directory_permission(value.c_str())) {
+ it.second.result = false;
+ new_line = true;
}
- if (stat_buffer.st_mode & S_IXOTH) {
- permission_map[name][2] = '1';
+ } else {
+ // go through all files to check permission
+ if (ftw(value.c_str(), permission_handler, OPEN_MAX_FILE) != 0) {
+ it.second.result = false;
+ new_line = true;
}
}
}
+ if (new_line) {
+ std::cout << std::endl; // print a new line after the failed permission
messages
+ }
+}
+
+// the fixing permission of runroot used by verify command
+static void
+fix_runroot(PermissionMapType &permission_map, const struct passwd *pwd)
+{
+ fix_flag = true;
+ for (const auto &it : permission_map) {
+ std::string name = it.first;
+ std::string value = it.second.path;
+ permission_entry = permission_map[name];
+ ftw(value.c_str(), permission_handler, OPEN_MAX_FILE);
+ }
+ fix_flag = false;
+ set_permission(permission_map, pwd);
}
void
LayoutEngine::verify_runroot()
{
+ // require sudo permission for --fix
+ if (arguments.get("fix") && getuid() != 0) {
+ ink_error("To fix permission issues, root privilege is required.\nPlease
run with sudo.");
+ status_code = EX_SOFTWARE;
+ return;
+ }
+
+ // retrieve information
std::string path = path_handler(arguments.get("path").value(), false,
_argv[0]);
std::string user;
char user_buf[MAX_LOGIN + 1];
@@ -656,49 +667,53 @@ LayoutEngine::verify_runroot()
user = TS_PKGSYSUSER;
}
}
+ // Numeric user notation for user
+ if (user[0] == '#') {
+ struct passwd *pwd = getpwuid(atoi(&user[1]));
+ if (!pwd) {
+ ink_error("No user found under id '%s'", user.c_str());
+ status_code = EX_OSERR;
+ return;
+ }
+ user = pwd->pw_name;
+ }
+ std::cout << "Verifying permission as user: \x1b[1m" << user << "\x1b[0m" <<
std::endl << std::endl;
+ // try to find the user from password file
+ struct passwd *pwd = getpwnam(user.c_str());
+ if (!pwd) {
+ ink_error("No user found under name '%s'", user.c_str());
+ status_code = EX_OSERR;
+ return;
+ }
// put paths from yaml file or default paths to path_map
RunrootMapType path_map;
if (path.empty()) {
path_map = runroot_map_default();
- std::cout << "Verifying default build ..." << std::endl;
} else {
path_map = runroot_map(Layout::relative_to(path, "runroot.yaml"));
}
-
- RunrootMapType permission_map; // contains rwx bits
- RunrootMapType usergroup_map; // map: owner, group, others
-
- set_permission(dir_vector, path_map, permission_map, usergroup_map, user);
-
- std::cout << "Verifying as user: " << user << std::endl << std::endl;
-
- // if --fix is used
- if (arguments.get("fix")) {
- fix_runroot(path_map, permission_map, usergroup_map);
- set_permission(dir_vector, path_map, permission_map, usergroup_map, user);
- } else {
- std::cout << "Note: This only shows directory permissions, please use
'--fix' to correct permissions on all files" << std::endl;
+ // setup the permission map
+ PermissionMapType permission_map;
+ for (const auto &it : dir_vector) {
+ permission_map[it].name = it;
+ permission_map[it].path = path_map[it];
+ }
+ // root always has full access and no need to check for root
+ if (user != "root") {
+ set_permission(permission_map, pwd);
+ // if --fix is used
+ if (arguments.get("fix")) {
+ fix_runroot(permission_map, pwd);
+ }
}
// display pass or fail for permission required
- for (uint i = 2; i < dir_vector.size(); i++) {
- std::string name = dir_vector[i];
- std::string permission = permission_map[dir_vector[i]];
- std::cout << name << ": \x1b[1m" + path_map[name] + "\x1b[0m" << std::endl;
-
- // output read permission
- if (name == LAYOUT_LOCALSTATEDIR || name == LAYOUT_INCLUDEDIR || name ==
LAYOUT_SYSCONFDIR || name == LAYOUT_DATADIR) {
- output_read_permission(permission);
- }
- // output write permission
- if (name == LAYOUT_LOGDIR || name == LAYOUT_RUNTIMEDIR || name ==
LAYOUT_CACHEDIR) {
- output_read_permission(permission);
- output_write_permission(permission);
- }
- // output execute permission
- if (name == LAYOUT_BINDIR || name == LAYOUT_SBINDIR || name ==
LAYOUT_LIBDIR || name == LAYOUT_LIBEXECDIR) {
- output_read_permission(permission);
- output_execute_permission(permission);
+ for (const auto &it : dir_vector) {
+ if (permission_map[it].result) {
+ std::cout << it << ": \x1b[1m" << path_map[it] << "\x1b[0m
\033[1;32mPASSED\033[0m" << std::endl;
+ } else {
+ std::cout << it << ": \x1b[1m" << path_map[it] << "\x1b[0m
\033[1;31mFAILED\033[0m" << std::endl;
+ status_code = EX_SOFTWARE;
}
}
}
diff --git a/src/traffic_layout/engine.h b/src/traffic_layout/engine.h
index 9154f1b..769b6e7 100644
--- a/src/traffic_layout/engine.h
+++ b/src/traffic_layout/engine.h
@@ -26,7 +26,21 @@
#include "tscore/ArgParser.h"
#include <unordered_map>
-typedef std::unordered_map<std::string, std::string> RunrootMapType;
+// used by runroot verify
+struct PermissionEntry {
+ std::string name; // sysconfdir, libdir ...
+ std::string path; // real path of the directory
+ // required permission
+ mode_t r_mode;
+ mode_t w_mode;
+ mode_t e_mode;
+ // result set by set_permission()
+ bool result = true;
+};
+
+// this map contain the corresponding permission information of directories
+// PermissionEntry contains the read/write/execute mode and the result of
output
+using PermissionMapType = std::unordered_map<std::string, PermissionEntry>;
// structure for informaiton of the runroot passing around
struct LayoutEngine {
@@ -49,4 +63,6 @@ struct LayoutEngine {
ts::Arguments arguments;
// mordern argv
std::vector<std::string> _argv;
+
+ int status_code = 0;
};
diff --git a/src/traffic_layout/traffic_layout.cc
b/src/traffic_layout/traffic_layout.cc
index 72edd20..fb26e34 100644
--- a/src/traffic_layout/traffic_layout.cc
+++ b/src/traffic_layout/traffic_layout.cc
@@ -74,5 +74,5 @@ main(int argc, const char **argv)
engine.arguments.invoke();
- return 0;
+ return engine.status_code;
}
diff --git a/src/tscore/ArgParser.cc b/src/tscore/ArgParser.cc
index 6f3f477..47e8c2e 100644
--- a/src/tscore/ArgParser.cc
+++ b/src/tscore/ArgParser.cc
@@ -34,6 +34,10 @@ std::string global_usage;
std::string parser_program_name;
std::string default_command;
+// by default return EX_USAGE(64) when usage is called.
+// if -h or --help is called specifically, return 0
+int usage_return_code = EX_USAGE;
+
namespace ts
{
ArgParser::ArgParser() {}
@@ -107,7 +111,7 @@ ArgParser::Command::help_message(std::string_view err) const
std::cout << "\nExample Usage: " << _example_usage << std::endl;
}
// standard return code
- exit(EX_USAGE);
+ exit(usage_return_code);
}
void
@@ -436,6 +440,7 @@ ArgParser::Command::append_option_data(Arguments &ret,
AP_StrVec &args, int inde
}
command = &it->second;
}
+ usage_return_code = 0;
command->help_message();
}
// deal with normal --arg val1 val2 ...
diff --git a/tests/gold_tests/runroot/runroot_init.test.py
b/tests/gold_tests/runroot/runroot_init.test.py
index ec8a2f6..5e8e371 100644
--- a/tests/gold_tests/runroot/runroot_init.test.py
+++ b/tests/gold_tests/runroot/runroot_init.test.py
@@ -59,7 +59,6 @@ tr.Processes.Default.Command = "mkdir " + path4 + ";touch " +
randomfile + ";$AT
tr.Processes.Default.ReturnCode = 0
f = tr.Disk.File(os.path.join(path4, "runroot.yaml"))
f.Exists = True
-tr.Processes.Default.Streams.All = Testers.ContainsExpression("Forcing
creating runroot", "force message")
# create runroot with junk to guarantee only traffic server related files are
copied
bin_path =
Test.Variables.BINDIR[Test.Variables.BINDIR.find(Test.Variables.PREFIX) +
len(Test.Variables.PREFIX) + 1:]
diff --git a/tests/gold_tests/runroot/runroot_verify.test.py
b/tests/gold_tests/runroot/runroot_verify.test.py
index a9253b4..d98058c 100644
--- a/tests/gold_tests/runroot/runroot_verify.test.py
+++ b/tests/gold_tests/runroot/runroot_verify.test.py
@@ -42,12 +42,7 @@ tr.Processes.Default.Streams.All =
Testers.ContainsExpression(
os.path.join(path, "bin"), "example bindir output")
tr.Processes.Default.Streams.All = Testers.ContainsExpression(
os.path.join(path, "var/log/trafficserver"), "example logdir output")
-tr.Processes.Default.Streams.All = Testers.ContainsExpression(
- "Read Permission: ", "read permission output")
-tr.Processes.Default.Streams.All = Testers.ContainsExpression(
- "Execute Permission: ", "execute permission output")
-tr.Processes.Default.Streams.All = Testers.ContainsExpression(
- "Write Permission: ", "write permission output")
+tr.Processes.Default.Streams.All = Testers.ContainsExpression("PASSED",
"contain passed message")
# verify test #2
bin_path = Test.Variables.BINDIR[Test.Variables.BINDIR.find(
@@ -60,9 +55,5 @@ tr.Processes.Default.Streams.All = Testers.ContainsExpression(
os.path.join(path, "bin"), "example bindir output")
tr.Processes.Default.Streams.All = Testers.ContainsExpression(
os.path.join(path, "var/log/trafficserver"), "example logdir output")
-tr.Processes.Default.Streams.All = Testers.ContainsExpression(
- "Read Permission: ", "read permission output")
-tr.Processes.Default.Streams.All = Testers.ContainsExpression(
- "Execute Permission: ", "execute permission output")
-tr.Processes.Default.Streams.All = Testers.ContainsExpression(
- "Write Permission: ", "write permission output")
+tr.Processes.Default.Streams.All = Testers.ContainsExpression("PASSED",
"contain passed message")
+