You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by dr...@apache.org on 2018/01/02 17:38:32 UTC

[trafficserver] branch master updated: Restructured traffic_layout and command line

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 24c57a2  Restructured traffic_layout and command line
24c57a2 is described below

commit 24c57a2056a0936063f616beeb51b8c5904a436a
Author: Xavier Chi <ch...@gmail.com>
AuthorDate: Thu Dec 21 16:30:22 2017 -0600

    Restructured traffic_layout and command line
---
 cmd/traffic_layout/Makefile.am                    |   4 +-
 cmd/traffic_layout/engine.cc                      | 366 +++++++++++++++-------
 cmd/traffic_layout/engine.h                       |  32 +-
 cmd/traffic_layout/file_system.cc                 |  45 ++-
 cmd/traffic_layout/file_system.h                  |   3 +
 cmd/traffic_layout/{traffic_layout.cc => info.cc} | 130 +-------
 lib/ts/runroot.h => cmd/traffic_layout/info.h     |  17 +-
 cmd/traffic_layout/traffic_layout.cc              | 275 +++++-----------
 doc/appendices/command-line/traffic_layout.en.rst |  43 +--
 lib/ts/I_Layout.h                                 |   7 -
 lib/ts/Layout.cc                                  |  85 +----
 lib/ts/runroot.cc                                 |  60 +++-
 lib/ts/runroot.h                                  |   9 +-
 tests/gold_tests/basic/runroot-layout.test.py     |  42 +--
 14 files changed, 547 insertions(+), 571 deletions(-)

diff --git a/cmd/traffic_layout/Makefile.am b/cmd/traffic_layout/Makefile.am
index 4bb3f2a..7d43f90 100644
--- a/cmd/traffic_layout/Makefile.am
+++ b/cmd/traffic_layout/Makefile.am
@@ -32,7 +32,9 @@ traffic_layout_SOURCES = \
   file_system.cc \
   file_system.h \
   engine.cc \
-  engine.h
+  engine.h \
+  info.cc \
+  info.h
 
 traffic_layout_LDADD = \
   $(top_builddir)/lib/records/librecords_p.a \
diff --git a/cmd/traffic_layout/engine.cc b/cmd/traffic_layout/engine.cc
index 2879634..94aede4 100644
--- a/cmd/traffic_layout/engine.cc
+++ b/cmd/traffic_layout/engine.cc
@@ -30,13 +30,16 @@
 #include "ts/ink_args.h"
 #include "ts/I_Version.h"
 #include "ts/ink_file.h"
-#include "ts/ink_assert.h"
+#include "records/I_RecCore.h"
 
 #include "engine.h"
 #include "file_system.h"
 
 #include <fstream>
 #include <iostream>
+#include <ftw.h>
+
+std::string directory_check = "";
 
 // check if user want to force create the ts_runroot
 // return true if user replies Y
@@ -45,7 +48,7 @@ check_force()
 {
   // check for Y/N 3 times
   for (int i = 0; i < 3; i++) {
-    std::cout << "Are you sure to overwrite and force creating runroot? (irreversible) Y/N: ";
+    std::cout << "Are you sure to overwrite and force creating/removing runroot? (irreversible) Y/N: ";
     std::string input;
     std::cin >> input;
     if (input == "Y" || input == "y")
@@ -60,46 +63,43 @@ check_force()
 // 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 int forceflag)
+check_run_path(const std::string &arg, const bool forceflag)
 {
-  if (arg.empty() || arg[0] == '-')
+  if (arg.empty()) {
     return false;
-  if (arg[0] != '-' && arg[0] != '/')
-    ink_fatal("Please provide absolute path");
-
-  std::string path = arg;
-  // check force create
-  if (forceflag == 1) {
+  }
+  // the condition of force create
+  if (exists(arg) && is_directory(arg) && forceflag) {
     if (!check_force()) {
-      ink_notice("Force create failed");
+      std::cout << "Force create terminated" << std::endl;
       exit(0);
     }
-    ink_notice("Forcing creating runroot ...");
-    if (!remove_directory(path)) {
-      ink_warning("Failed removing(overwriting) existing directory - %s", strerror(errno));
-    }
+    std::cout << "Forcing creating runroot ..." << std::endl;
+    // directory_remove = arg;
+    // nftw(arg.c_str(), remove_inside_directory, OPEN_MAX_FILE, FTW_DEPTH);
+    return true;
   }
+
   // if directory already exist
-  if (exists(path) && is_directory(path)) {
+  if (exists(arg) && is_directory(arg)) {
     return true;
   } else {
     // try to create & remove
-    if (!create_directory(path)) {
+    if (!create_directory(arg)) {
       return false;
     }
-    remove_directory(path);
+    remove_directory(arg);
     return true;
   }
 }
 
 // return true if the path is good to delete
 static bool
-check_delete_path(const std::string &arg)
+check_delete_path(const std::string &arg, const bool forceflag)
 {
-  if (arg.empty() || arg[0] == '-')
+  if (arg.empty()) {
     return false;
-  if (arg[0] != '-' && arg[0] != '/')
-    ink_fatal("Please provide absolute path");
+  }
 
   std::ifstream check_file(arg);
   if (check_file) {
@@ -108,104 +108,159 @@ check_delete_path(const std::string &arg)
   return false;
 }
 
-// the help message for traffic_runroot
-static void
-help_message(const int versionflag, const int runflag, const int cleanflag, const int forceflag)
+// handle the path of the engine during parsing
+static std::string
+path_handler(std::string &path)
 {
-  std::cout << "if no path provided, please set Environment variable $TS_RUNROOT" << std::endl;
-  std::cout << "traffic_layout runroot usage: traffic_layout [switch] [<path>]" << std::endl;
-  std::cout << "                       traffic_layout --force [switch] [<path>]\n" << std::endl;
-  std::cout << "==option=====switch=====description=====================================" << std::endl;
-  std::cout << "Run:      --init(-i)     (Initialize the ts_runroot sandbox)" << std::endl;
-  std::cout << "Remove:   --remove(-r)   (remove the ts_runroot sandbox)\n" << std::endl;
-  std::cout << "==flag=======key=========description======================================" << std::endl;
-  std::cout << "force:    --force   (force to create ts_runroot, only works with init)\n" << std::endl;
-  std::cout << "Program information: traffic_layout [switch] -h" << std::endl;
-
-  if (runflag)
-    std::cout << "\ninit example: traffic_layout --init(-i) /path/to/sandbox" << std::endl;
-  if (cleanflag)
-    std::cout << "\nremove example: traffic_layout --remove(-r) /path/to/sandbox" << std::endl;
-  if (forceflag)
-    std::cout << "\nforce example: traffic_layout --force init /path/to/sandbox" << std::endl;
+  if (path[0] == '/') {
+    return path;
+  }
+  if (path.empty()) {
+    char cwd[PATH_MAX];
+    getcwd(cwd, sizeof(cwd));
+    return cwd;
+  }
+  char resolved_path[PATH_MAX];
+  realpath(path.c_str(), resolved_path);
+
+  return resolved_path;
+}
+
+// the help message for traffic_layout runroot
+void
+RunrootEngine::runroot_help_message(const bool runflag, const bool cleanflag)
+{
+  if (runflag) {
+    std::cout << "\ninit Usage: traffic_layout init ([switch]) --path /path/to/sandbox\n"
+                 "Note : if no path provided, please set Environment variable $TS_RUNROOT\n"
+              << std::endl;
+    std::cout << "Sub-switches:\n"
+                 "--path        Specify the path of the runroot (the path should be the next argument)\n"
+                 "--force       Force to create ts_runroot even directory already exists\n"
+                 "--absolute    Produce absolute path in the yaml file\n"
+                 "--run-root(=/path)  Using specified TS_RUNROOT as sandbox\n"
+              << std::endl;
+  }
+  if (cleanflag) {
+    std::cout << "\nremove Usage: traffic_layout remove ([switch]) --path /path/to/sandbox\n"
+                 "Note : if no path provided, please set Environment variable $TS_RUNROOT\n"
+              << std::endl;
+    std::cout << "Sub-switches:\n"
+                 "--path       specify the path of the runroot (the path should be the next argument)\n"
+                 "--force      force to remove ts_runroot even with  other unknown files\n"
+              << std::endl;
+  }
+  return;
 }
 
 // the parsing function for traffic runroot program
 // set the flag & path appropriately
-void
+bool
 RunrootEngine::runroot_parse()
 {
-  int i = 0;
+  int i = 1;
   while (i < _argc) {
     std::string argument = _argv[i];
-    // set help, verion, force flag
+    // set the help, version, force and absolute flags
     if (argument == "-h" || argument == "--help") {
-      help_flag = 1;
+      help_flag = true;
+      ++i;
+      continue;
     }
     if (argument == "-V" || argument == "--version") {
-      version_flag = 1;
+      version_flag = true;
+      ++i;
+      continue;
     }
     if (argument == "--force") {
-      force_flag = 1;
+      force_flag = true;
+      ++i;
+      continue;
+    }
+    if (argument == "--absolute") {
+      abs_flag = true;
+      ++i;
+      continue;
+    }
+    if (argument.substr(0, RUNROOT_WORD_LENGTH) == "--run-root") {
+      ++i;
+      continue;
     }
     // set init flag & sandbox path
-    if (argument == "--init" || argument == "-i") {
-      run_flag = 1;
-      if (i == _argc - 1)
-        break;
-      if (!check_run_path(_argv[i + 1], force_flag)) {
-        ++i;
-        continue;
-      }
-      run_path = _argv[i + 1];
+    if (argument == "init") {
+      run_flag = true;
       ++i;
+      continue;
     }
     // set remove flag & sandbox path
-    if (argument == "--remove" || argument == "-r") {
-      clean_flag = 1;
-      if (i == _argc - 1)
-        break;
-      if (!check_delete_path(_argv[i + 1])) {
-        ++i;
-        continue;
-      }
-      clean_path = _argv[i + 1];
+    if (argument == "remove") {
+      clean_flag = true;
       ++i;
+      continue;
+    }
+    if (argument == "--path") {
+      if (i + 1 >= static_cast<int>(_argv.size()) || _argv[i + 1][0] == '-') {
+        // invalid path
+        return false;
+      }
+      path = _argv[i + 1];
+      i += 2;
+      continue;
     }
-    ++i;
+    return false;
   }
+  return true;
+}
+
+// check the logic and see if everthing is fine
+void
+RunrootEngine::sanity_check()
+{
   // check output help or not
-  if (help_flag == 1) {
-    help_message(version_flag, run_flag, clean_flag, force_flag);
+  if (help_flag) {
+    runroot_help_message(run_flag, clean_flag);
     exit(0);
   }
-  if (version_flag == 1) {
+  if (version_flag) {
     // get version info
     AppVersionInfo appVersionInfo;
-    appVersionInfo.setup(PACKAGE_NAME, "traffic_runroot", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
+    appVersionInfo.setup(PACKAGE_NAME, "traffic_layout", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
     ink_fputln(stdout, appVersionInfo.FullVersionInfoStr);
     exit(0);
   }
-  if (run_flag == 1 && clean_flag == 1) {
+  if (run_flag && clean_flag) {
     ink_fatal("Cannot run and clean in the same time");
   }
-  if (force_flag == 1 && run_flag == 0) {
+  if (!run_flag && !clean_flag) {
+    ink_fatal("No command specified");
+  }
+  if ((force_flag && !run_flag) && (force_flag && !clean_flag)) {
     ink_fatal("Nothing to force");
   }
-}
 
-// for cleaning the parent of bin / cwd
+  path = path_handler(path);
+
+  if (run_flag) {
+    if (!check_run_path(path, force_flag)) {
+      ink_fatal("Invalid path to create: %s", path.c_str());
+    }
+  }
+  if (clean_flag) {
+    if (!check_delete_path(path, force_flag)) {
+      ink_fatal("Invalid path to remove: %s", path.c_str());
+    }
+  }
+}
+// for cleanning the parent of bin / cwd
 // return the path if we can clean the bin / cwd
 const static std::string
 clean_parent(const std::string &bin_path)
 {
-  char cwd[MAX_CWD_LEN];
-  ink_release_assert(getcwd(cwd, sizeof(cwd)) == nullptr);
+  char cwd[PATH_MAX];
+  getcwd(cwd, sizeof(cwd));
 
-  char resolved_binpath[MAX_CWD_LEN];
-  if (realpath(bin_path.c_str(), resolved_binpath) == nullptr) { // bin path
-    return "";
-  }
+  char resolved_binpath[PATH_MAX];
+  realpath(bin_path.c_str(), resolved_binpath); // bin path
   std::string RealBinPath = resolved_binpath;
 
   std::vector<std::string> TwoPath = {RealBinPath, cwd};
@@ -219,39 +274,119 @@ clean_parent(const std::string &bin_path)
 }
 
 // the function for removing the runroot
-bool
+void
 RunrootEngine::clean_runroot()
 {
-  if (clean_flag == 1) {
-    std::string clean_root;
-    if (!clean_path.empty()) {
-      clean_root = clean_path;
+  std::string clean_root = path;
+
+  char cwd[PATH_MAX];
+  getcwd(cwd, sizeof(cwd));
+  std::string cur_working_dir = cwd;
+
+  if (force_flag) {
+    std::ifstream check_file(Layout::relative_to(clean_root, "runroot_path.yml"));
+    if (!check_file.good()) {
+      ink_fatal("invalid path to clean (no runroot_path.yml file found)");
+    }
+    // the force clean
+    if (!check_force()) {
+      std::cout << "Force remove terminated" << std::endl;
+      exit(0);
+    }
+    std::cout << "Forcing removing runroot ..." << std::endl;
+    if (cur_working_dir == path) {
+      // if cwd is the runroot, keep the directory and remove everything insides
+      remove_inside_directory(clean_root);
     } else {
-      // no clean path provided get the environment
-      if (getenv("TS_RUNROOT") != nullptr) {
-        clean_root = getenv("TS_RUNROOT");
-      } else {
-        // no path & environment, get parents of bin/cwd
-        clean_root = clean_parent(_argv[0]);
-        if (clean_root.empty())
-          ink_fatal("Nothing to clean");
+      if (!remove_directory(clean_root)) {
+        ink_warning("Failed force removing runroot: %s - %s", clean_root.c_str(), strerror(errno));
       }
     }
-
+  } else {
     // if we can find the yaml, then clean it
     std::ifstream check_file(Layout::relative_to(clean_root, "runroot_path.yml"));
-    if (check_file.good()) {
-      if (!remove_directory(clean_root)) {
-        ink_fatal("Error cleaning directory - %s", strerror(errno));
+    if (check_file.good() || !clean_parent(_argv[0]).empty()) {
+      if (!clean_parent(_argv[0]).empty()) {
+        clean_root = clean_parent(_argv[0]);
+      }
+      // handle the map and deleting of each directories specified in the yml file
+      std::string yaml_file = Layout::relative_to(clean_root, "runroot_path.yml");
+      std::unordered_map<std::string, std::string> map = runroot_map(yaml_file, clean_root);
+      map.erase("prefix");
+      map.erase("exec_prefix");
+      append_slash(clean_root);
+      for (auto it : map) {
+        std::string dir = it.second;
+        append_slash(dir);
+        std::string later_dir = dir.substr(clean_root.size());
+        dir                   = Layout::relative_to(clean_root, later_dir.substr(0, later_dir.find_first_of("/")));
+        remove_directory(dir);
+      }
+      remove(yaml_file.c_str());
+      if (cur_working_dir != path) {
+        // if the runroot is empty, remove it
+        remove(clean_root.c_str());
       }
     } else {
       ink_fatal("invalid path to clean (no runroot_path.yml file found)");
     }
-    return true;
   }
+}
 
-  // no clean
-  return false;
+// if directory is not empty, throw error
+static int
+check_directory(const char *path, const struct stat *s, int flag, struct FTW *f)
+{
+  std::string checkpath = path;
+  if (checkpath != directory_check) {
+    ink_fatal("directory not empty, use --force to overwrite");
+  }
+  return 0;
+}
+
+// the function for creating the runroot
+void
+RunrootEngine::create_runroot()
+{
+  // start the runroot creating stuff
+  std::string original_root;
+  char *env_val = getenv("TS_RUNROOT");
+  if ((env_val != nullptr) && is_directory(env_val)) {
+    // from env variable
+    original_root = env_val;
+  } else {
+    // from default layout structure
+    original_root = Layout::get()->prefix;
+  }
+  std::string ts_runroot = path;
+
+  // handle the ts_runroot
+  // ts runroot must be an accessible path
+  std::ifstream check_file(Layout::relative_to(ts_runroot, "runroot_path.yml"));
+  if (check_file.good()) {
+    // if the path already ts_runroot, use it rather than create new one
+    std::cout << "Using existing TS_RUNROOT..." << std::endl;
+    std::cout << "Please remove the old TS_RUNROOT if new runroot is needed \n(usage: traffic_layout remove --path /path/...)"
+              << std::endl;
+    return;
+  } else if (exists(ts_runroot) && is_directory(ts_runroot) && !force_flag) {
+    // check if directory is empty
+    directory_check = ts_runroot;
+    nftw(ts_runroot.c_str(), check_directory, OPEN_MAX_FILE, FTW_DEPTH);
+  }
+
+  // create new root & copy from original to new runroot. then fill in the map
+  copy_runroot(original_root, ts_runroot);
+
+  // create and emit to yaml file the key value pairs of path
+  std::ofstream yamlfile;
+  std::string yaml_path = Layout::relative_to(ts_runroot, "runroot_path.yml");
+  yamlfile.open(yaml_path);
+
+  for (auto it : path_map) {
+    // out put key value pairs of path
+    yamlfile << it.first << ": " << it.second << std::endl;
+  }
 }
 
 // copy the stuff from original_root to ts_runroot
@@ -274,21 +409,38 @@ RunrootEngine::copy_runroot(const std::string &original_root, const std::string
   original_map["runtimedir"]    = TS_BUILD_RUNTIMEDIR;
   original_map["logdir"]        = TS_BUILD_LOGDIR;
   original_map["mandir"]        = TS_BUILD_MANDIR;
-  original_map["infodir"]       = TS_BUILD_INFODIR;
   original_map["cachedir"]      = TS_BUILD_CACHEDIR;
 
   // copy each directory to the runroot path
   // symlink the executables
   // set up path_map for yaml to emit key-value pairs
-  ink_notice("Copying from the original root...");
-
   for (auto it : original_map) {
-    std::string old_path = Layout::relative_to(original_root, it.second);
-    std::string new_path = Layout::relative_to(ts_runroot, it.second);
+    // path handle
+    std::string join_path;
+    if (it.second[0] && it.second[0] == '/') {
+      join_path = it.second.substr(1);
+    } else {
+      join_path = it.second;
+    }
+
+    std::string old_path = Layout::relative_to(original_root, join_path);
+    std::string new_path = Layout::relative_to(ts_runroot, join_path);
+    if (abs_flag) {
+      path_map[it.first] = Layout::relative_to(ts_runroot, join_path);
+    } else {
+      path_map[it.first] = Layout::relative_to(".", join_path);
+    }
+
     if (!copy_directory(old_path, new_path)) {
       ink_warning("Copy failed for %s - %s", it.first.c_str(), strerror(errno));
     }
-    path_map[it.first] = new_path;
   }
-  path_map["prefix"] = ts_runroot;
+
+  std::cout << "Copying from " + original_root + " ..." << std::endl;
+
+  if (abs_flag) {
+    path_map["prefix"] = ts_runroot;
+  } else {
+    path_map["prefix"] = ".";
+  }
 }
diff --git a/cmd/traffic_layout/engine.h b/cmd/traffic_layout/engine.h
index c68ef51..0cb474b 100644
--- a/cmd/traffic_layout/engine.h
+++ b/cmd/traffic_layout/engine.h
@@ -27,31 +27,41 @@
 #include <string>
 #include <unordered_map>
 
+#define RUNROOT_WORD_LENGTH 10
+
 // structure for informaiton of the runroot passing around
 struct RunrootEngine {
   // the parsing function for traffic runroot program
-  void runroot_parse();
+  bool runroot_parse();
+
+  // check the logic and see if everthing is fine
+  void sanity_check();
 
   // the function for removing the runroot
-  // return true upon success, false upon failure
-  bool clean_runroot();
+  void clean_runroot();
+
+  // the function of creating runroot
+  void create_runroot();
 
   // copy the stuff from original_root to ts_runroot
   // fill in the global map for yaml file emitting later
   void copy_runroot(const std::string &original_root, const std::string &ts_runroot);
 
+  // the help message for runroot
+  void runroot_help_message(const bool runflag, const bool cleanflag);
+
   // the pass in arguments
   int _argc;
   std::vector<std::string> _argv;
-  // the flag for command line parsing
-  int help_flag    = 0;
-  int version_flag = 0;
-  int run_flag     = 0;
-  int clean_flag   = 0;
-  int force_flag   = 0;
+  // flags for command line parsing
+  bool help_flag    = false;
+  bool version_flag = false;
+  bool run_flag     = false;
+  bool clean_flag   = false;
+  bool force_flag   = false;
+  bool abs_flag     = false;
   // the path for create & remove
-  std::string run_path;
-  std::string clean_path;
+  std::string path;
 
   // map for yaml file emit
   std::unordered_map<std::string, std::string> path_map;
diff --git a/cmd/traffic_layout/file_system.cc b/cmd/traffic_layout/file_system.cc
index c4cba3d..e743b07 100644
--- a/cmd/traffic_layout/file_system.cc
+++ b/cmd/traffic_layout/file_system.cc
@@ -25,7 +25,7 @@
 // including: make directory (with parents), copy directory (recursively), remove directory (recursively)
 
 #include "ts/ink_error.h"
-
+#include "ts/I_Layout.h"
 #include "file_system.h"
 
 #include <iostream>
@@ -34,11 +34,11 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <ts/I_Layout.h>
 
 // global variables for copy function
 static std::string dst_root;
 static std::string src_root;
+static std::string remove_path;
 
 void
 append_slash(std::string &path)
@@ -121,6 +121,28 @@ remove_function(const char *path, const struct stat *s, int flag, struct FTW *f)
   return 0;
 }
 
+static int
+remove_inside_function(const char *path, const struct stat *s, int flag, struct FTW *f)
+{
+  std::string path_to_remove = path;
+  if (path_to_remove != remove_path) {
+    switch (flag) {
+    default:
+      if (remove(path) != 0) {
+        ink_error("unable to remove: %s", path);
+        return -1;
+      }
+      break;
+    case FTW_DP:
+      if (!remove_directory(path_to_remove)) {
+        ink_error("unable to remove: %s", path);
+        return -1;
+      }
+    }
+  }
+  return 0;
+}
+
 // remove directory recursively using nftw to iterate
 bool
 remove_directory(const std::string &dir)
@@ -133,6 +155,19 @@ remove_directory(const std::string &dir)
     return true;
 }
 
+// remove everything inside this directory
+bool
+remove_inside_directory(const std::string &dir)
+{
+  std::string path = dir;
+  remove_slash(path);
+  remove_path = path;
+  if (nftw(path.c_str(), remove_inside_function, OPEN_MAX_FILE, FTW_DEPTH))
+    return false;
+  else
+    return true;
+}
+
 static int
 copy_function(const char *src_path, const struct stat *sb, int flag)
 {
@@ -153,6 +188,12 @@ copy_function(const char *src_path, const struct stat *sb, int flag)
       ink_fatal("create directory failed during copy");
     break;
   case FTW_F:
+    // if the file already exist, overwrite it
+    if (exists(dst_path)) {
+      if (remove(dst_path.c_str())) {
+        ink_error("overwrite file falied during copy");
+      }
+    }
     // for files if bin executable mode, symlink
     if (sb->st_mode == BIN_MODE) {
       if (symlink(src_path, dst_path.c_str()) != 0) {
diff --git a/cmd/traffic_layout/file_system.h b/cmd/traffic_layout/file_system.h
index 093f60b..985220e 100644
--- a/cmd/traffic_layout/file_system.h
+++ b/cmd/traffic_layout/file_system.h
@@ -47,4 +47,7 @@ bool create_directory(const std::string &dir);
 
 bool remove_directory(const std::string &dir);
 
+// remove everything inside this directory
+bool remove_inside_directory(const std::string &dir);
+
 bool copy_directory(const std::string &src, const std::string &dst);
diff --git a/cmd/traffic_layout/traffic_layout.cc b/cmd/traffic_layout/info.cc
similarity index 59%
copy from cmd/traffic_layout/traffic_layout.cc
copy to cmd/traffic_layout/info.cc
index c0d2ef2..7f45541 100644
--- a/cmd/traffic_layout/traffic_layout.cc
+++ b/cmd/traffic_layout/info.cc
@@ -21,39 +21,10 @@
   limitations under the License.
  */
 
-#include "ts/ink_platform.h"
-#include "ts/ink_args.h"
-#include "ts/I_Version.h"
 #include "ts/I_Layout.h"
 #include "I_RecProcess.h"
 #include "RecordsConfig.h"
-#include "ts/runroot.h"
-#include "engine.h"
-#include "file_system.h"
-
-#include <iostream>
-#include <fstream>
-
-// Command line arguments (parsing)
-struct CommandLineArgs {
-  int layout;
-  int features;
-  int json;
-};
-
-static CommandLineArgs cl;
-
-const ArgumentDescription argument_descriptions[] = {
-  {"layout", 'l', "Show the layout (this is the default with no options given)", "T", &cl.layout, nullptr, nullptr},
-  {"features", 'f', "Show the compiled features", "T", &cl.features, nullptr, nullptr},
-  {"json", 'j', "Produce output in JSON format (when supported)", "T", &cl.json, nullptr, nullptr},
-  {"init", 'i', "Initialize the ts_runroot sandbox (details in traffic_layout --init -h)", nullptr, nullptr, nullptr, nullptr},
-  {"remove", 'r', "remove the ts_runroot sandbox (details in traffic_layout --remove -h)", nullptr, nullptr, nullptr, nullptr},
-  {"force", '-', "force flag for init (details in traffic_layout --force -h)", nullptr, nullptr, nullptr, nullptr},
-
-  HELP_ARGUMENT_DESCRIPTION(),
-  VERSION_ARGUMENT_DESCRIPTION(),
-  RUNROOT_ARGUMENT_DESCRIPTION()};
+#include "info.h"
 
 // Produce output about compile time features, useful for checking how things were built, as well
 // as for our TSQA test harness.
@@ -77,7 +48,7 @@ print_feature(const char *name, const char *value, bool json, bool last = false)
   }
 }
 
-static void
+void
 produce_features(bool json)
 {
   if (json) {
@@ -148,7 +119,7 @@ print_var(ts::string_view const &name, ts::string_view const &value, bool json,
     printf("%.*s: %.*s\n", static_cast<int>(name.size()), name.data(), static_cast<int>(value.size()), value.data());
 }
 
-static void
+void
 produce_layout(bool json)
 {
   Layout::create();
@@ -172,7 +143,6 @@ produce_layout(bool json)
   print_var("remap.config", RecConfigReadConfigPath("proxy.config.url_remap.filename"), json);
   print_var("plugin.config", RecConfigReadConfigPath(nullptr, "plugin.config"), json);
   print_var("ssl_multicert.config", RecConfigReadConfigPath("proxy.config.ssl.server.multicert.filename"), json);
-  print_var("ssl_server_name.config", RecConfigReadConfigPath("proxy.config.ssl.servername.filename"), json);
   print_var("storage.config", RecConfigReadConfigPath("proxy.config.cache.storage_filename"), json);
   print_var("hosting.config", RecConfigReadConfigPath("proxy.config.cache.hosting_filename"), json);
   print_var("volume.config", RecConfigReadConfigPath("proxy.config.cache.volume_filename"), json);
@@ -181,97 +151,3 @@ produce_layout(bool json)
     printf("}\n");
   }
 }
-
-void
-traffic_runroot(int argc, const char **argv)
-{
-  // runroot engine for operations
-  RunrootEngine engine;
-  engine._argc = argc;
-  int i        = 0;
-  while (argv[i]) {
-    engine._argv.push_back(argv[i]);
-    ++i;
-  }
-
-  // parse the command line & put into global variable
-  engine.runroot_parse();
-
-  // check to clean the runroot or not
-  if (engine.clean_runroot())
-    return;
-
-  // start the runroot creating stuff
-  std::string original_root = TS_BUILD_PREFIX;
-
-  // setting up ts_runroot
-  // Use passed in parameter, else use ENV variable
-  std::string ts_runroot;
-  if (!engine.run_path.empty()) {
-    ts_runroot = engine.run_path;
-  } else {
-    if (getenv("TS_RUNROOT") != nullptr) {
-      ts_runroot = getenv("TS_RUNROOT");
-      ink_notice("Using TS_RUNROOT Env variable");
-    } else {
-      ink_fatal("Invalid ts_runroot path\n(please set command line path or Environment variable $TS_RUNROOT)");
-    }
-  }
-
-  // handle the ts_runroot
-  // ts runroot must be an accessible path
-  std::ifstream check_file(Layout::relative_to(ts_runroot, "runroot_path.yml"));
-  if (check_file.good()) {
-    // if the path already ts_runroot, use it
-    ink_notice("Using existing TS_RUNROOT...");
-    ink_notice("Please remove the old TS_RUNROOT if new runroot is needed \n(usage: traffic_runroot rm /path/...)");
-    return;
-  } else if (exists(ts_runroot) && is_directory(ts_runroot)) {
-    ink_fatal("directory already exist");
-  }
-
-  // create new root & copy from original to new runroot. then fill in the map
-  engine.copy_runroot(original_root, ts_runroot);
-
-  // create and emit to yaml file the key value pairs of path
-  std::ofstream yamlfile;
-  std::string yaml_path = Layout::relative_to(ts_runroot, "runroot_path.yml");
-  yamlfile.open(yaml_path);
-
-  for (auto it : engine.path_map) {
-    // out put key value pairs of path
-    yamlfile << it.first << ": " << it.second << std::endl;
-  }
-  ink_notice("TS runroot initialized");
-
-  return;
-}
-
-int
-main(int argc, const char **argv)
-{
-  // check for traffic_runroot operations
-  for (int i = 0; i < argc; i++) {
-    if (!strcmp(argv[i], "--init") || !strcmp(argv[i], "--remove") || !strcmp(argv[i], "-i") || !strcmp(argv[i], "-r") ||
-        !strcmp(argv[i], "--force")) {
-      traffic_runroot(argc, argv);
-      exit(0);
-    }
-  }
-  // normal print out layout operation
-  AppVersionInfo appVersionInfo;
-
-  appVersionInfo.setup(PACKAGE_NAME, "traffic_layout", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
-
-  // Process command line arguments and dump into variables
-  process_args(&appVersionInfo, argument_descriptions, countof(argument_descriptions), argv);
-
-  runroot_handler(argv, 0 != cl.json);
-
-  if (cl.features) {
-    produce_features(0 != cl.json);
-  } else {
-    produce_layout(0 != cl.json);
-  }
-  exit(0);
-}
diff --git a/lib/ts/runroot.h b/cmd/traffic_layout/info.h
similarity index 73%
copy from lib/ts/runroot.h
copy to cmd/traffic_layout/info.h
index ee0118b..2230b88 100644
--- a/lib/ts/runroot.h
+++ b/cmd/traffic_layout/info.h
@@ -1,6 +1,6 @@
 /** @file
 
-  A brief file prefix
+  A brief file description
 
   @section license License
 
@@ -19,17 +19,10 @@
   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.
-*/
+ */
 
-// runroot handler for TS_RUNROOT
-// detailed information in runroot.cc
+// header file for traffic layout info
 
-#pragma once
+void produce_features(bool json);
 
-#include <string>
-
-#define MAX_CWD_LEN 1024
-
-std::string check_parent_path(const std::string &path, bool json = false);
-
-void runroot_handler(const char **argv, bool json = false);
+void produce_layout(bool json);
diff --git a/cmd/traffic_layout/traffic_layout.cc b/cmd/traffic_layout/traffic_layout.cc
index c0d2ef2..a163575 100644
--- a/cmd/traffic_layout/traffic_layout.cc
+++ b/cmd/traffic_layout/traffic_layout.cc
@@ -30,11 +30,19 @@
 #include "ts/runroot.h"
 #include "engine.h"
 #include "file_system.h"
+#include "info.h"
 
 #include <iostream>
 #include <fstream>
+#include <set>
 
-// Command line arguments (parsing)
+struct subcommand {
+  int (*handler)(int, const char **);
+  const std::string name;
+  const std::string help;
+};
+
+// // Command line arguments (parsing)
 struct CommandLineArgs {
   int layout;
   int features;
@@ -47,231 +55,118 @@ const ArgumentDescription argument_descriptions[] = {
   {"layout", 'l', "Show the layout (this is the default with no options given)", "T", &cl.layout, nullptr, nullptr},
   {"features", 'f', "Show the compiled features", "T", &cl.features, nullptr, nullptr},
   {"json", 'j', "Produce output in JSON format (when supported)", "T", &cl.json, nullptr, nullptr},
-  {"init", 'i', "Initialize the ts_runroot sandbox (details in traffic_layout --init -h)", nullptr, nullptr, nullptr, nullptr},
-  {"remove", 'r', "remove the ts_runroot sandbox (details in traffic_layout --remove -h)", nullptr, nullptr, nullptr, nullptr},
-  {"force", '-', "force flag for init (details in traffic_layout --force -h)", nullptr, nullptr, nullptr, nullptr},
-
-  HELP_ARGUMENT_DESCRIPTION(),
   VERSION_ARGUMENT_DESCRIPTION(),
   RUNROOT_ARGUMENT_DESCRIPTION()};
 
-// Produce output about compile time features, useful for checking how things were built, as well
-// as for our TSQA test harness.
-static void
-print_feature(const char *name, int value, bool json, bool last = false)
-{
-  if (json) {
-    printf("    \"%s\": %d%s", name, value, last ? "\n" : ",\n");
-  } else {
-    printf("#define %s %d\n", name, value);
-  }
-}
-
+// the usage help message for subcommand
 static void
-print_feature(const char *name, const char *value, bool json, bool last = false)
+help_usage()
 {
-  if (json) {
-    printf(R"(    "%s": "%s"%s)", name, value, last ? "\n" : ",\n");
-  } else {
-    printf("#define %s \"%s\"\n", name, value);
-  }
+  std::cout << "\nSubcommands:\n"
+               "init         Initialize the ts_runroot sandbox\n"
+               "remove       Remove the ts_runroot sandbox\n"
+               "info         Show the layout as default\n"
+            << std::endl;
+  std::cout << "Switches of runroot:\n"
+               "--path       Specify the path of the runroot\n"
+               "--force:     Force to create ts_runroot, only works with init\n"
+               "--absolute:  Produce absolute path in the yaml file\n"
+               "--run-root(=/path):  Using specified TS_RUNROOT as sandbox\n"
+            << std::endl;
+
+  printf("General Usage:\n");
+  usage(argument_descriptions, countof(argument_descriptions), nullptr);
 }
 
-static void
-produce_features(bool json)
+int
+info(int argc, const char **argv)
 {
-  if (json) {
-    printf("{\n");
+  // take the "info" out from command line
+  if (argv[1] && argv[1] == "info"_sv) {
+    for (int i = 1; i < argc; i++) {
+      argv[i] = argv[i + 1];
+    }
   }
-  print_feature("BUILD_MACHINE", BUILD_MACHINE, json);
-  print_feature("BUILD_PERSON", BUILD_PERSON, json);
-  print_feature("BUILD_GROUP", BUILD_GROUP, json);
-  print_feature("BUILD_NUMBER", BUILD_NUMBER, json);
-#ifdef HAVE_ZLIB_H
-  print_feature("TS_HAS_LIBZ", 1, json);
-#else
-  print_feature("TS_HAS_LIBZ", 0, json);
-#endif
-#ifdef HAVE_LZMA_H
-  print_feature("TS_HAS_LZMA", 1, json);
-#else
-  print_feature("TS_HAS_LZMA", 0, json);
-#endif
-  print_feature("TS_HAS_JEMALLOC", TS_HAS_JEMALLOC, json);
-  print_feature("TS_HAS_TCMALLOC", TS_HAS_TCMALLOC, json);
-  print_feature("TS_HAS_IN6_IS_ADDR_UNSPECIFIED", TS_HAS_IN6_IS_ADDR_UNSPECIFIED, json);
-  print_feature("TS_HAS_BACKTRACE", TS_HAS_BACKTRACE, json);
-  print_feature("TS_HAS_PROFILER", TS_HAS_PROFILER, json);
-  print_feature("TS_USE_FAST_SDK", TS_USE_FAST_SDK, json);
-  print_feature("TS_USE_DIAGS", TS_USE_DIAGS, json);
-  print_feature("TS_USE_EPOLL", TS_USE_EPOLL, json);
-  print_feature("TS_USE_KQUEUE", TS_USE_KQUEUE, json);
-  print_feature("TS_USE_PORT", TS_USE_PORT, json);
-  print_feature("TS_USE_POSIX_CAP", TS_USE_POSIX_CAP, json);
-  print_feature("TS_USE_TPROXY", TS_USE_TPROXY, json);
-  print_feature("TS_HAS_SO_MARK", TS_HAS_SO_MARK, json);
-  print_feature("TS_HAS_IP_TOS", TS_HAS_IP_TOS, json);
-  print_feature("TS_USE_HWLOC", TS_USE_HWLOC, json);
-  print_feature("TS_USE_TLS_NPN", TS_USE_TLS_NPN, json);
-  print_feature("TS_USE_TLS_ALPN", TS_USE_TLS_ALPN, json);
-  print_feature("TS_USE_TLS_SNI", TS_USE_TLS_SNI, json);
-  print_feature("TS_USE_CERT_CB", TS_USE_CERT_CB, json);
-  print_feature("TS_USE_SET_RBIO", TS_USE_SET_RBIO, json);
-  print_feature("TS_USE_TLS_ECKEY", TS_USE_TLS_ECKEY, json);
-  print_feature("TS_USE_LINUX_NATIVE_AIO", TS_USE_LINUX_NATIVE_AIO, json);
-  print_feature("TS_HAS_SO_PEERCRED", TS_HAS_SO_PEERCRED, json);
-  print_feature("TS_USE_REMOTE_UNWINDING", TS_USE_REMOTE_UNWINDING, json);
-  print_feature("SIZEOF_VOIDP", SIZEOF_VOIDP, json);
-  print_feature("TS_IP_TRANSPARENT", TS_IP_TRANSPARENT, json);
-  print_feature("TS_HAS_128BIT_CAS", TS_HAS_128BIT_CAS, json);
-  print_feature("TS_HAS_TESTS", TS_HAS_TESTS, json);
-  print_feature("TS_HAS_WCCP", TS_HAS_WCCP, json);
-  print_feature("TS_MAX_THREADS_IN_EACH_THREAD_TYPE", TS_MAX_THREADS_IN_EACH_THREAD_TYPE, json);
-  print_feature("TS_MAX_NUMBER_EVENT_THREADS", TS_MAX_NUMBER_EVENT_THREADS, json);
-  print_feature("TS_MAX_HOST_NAME_LEN", TS_MAX_HOST_NAME_LEN, json);
-  print_feature("TS_MAX_API_STATS", TS_MAX_API_STATS, json);
-  print_feature("SPLIT_DNS", SPLIT_DNS, json);
-  print_feature("TS_PKGSYSUSER", TS_PKGSYSUSER, json);
-  print_feature("TS_PKGSYSGROUP", TS_PKGSYSGROUP, json, true);
-  if (json) {
-    printf("}\n");
+  // detect help command
+  int i = 1;
+  while (argv[i]) {
+    if (argv[i] == "--help"_sv || argv[i] == "-h"_sv) {
+      help_usage();
+    }
+    ++i;
   }
-}
-
-void
-print_var(ts::string_view const &name, ts::string_view const &value, bool json, bool last = false)
-{
-  if (json)
-    printf(R"(    "%.*s": "%.*s"%s)", static_cast<int>(name.size()), name.data(), static_cast<int>(value.size()), value.data(),
-           last ? "\n" : ",\n");
-  else
-    printf("%.*s: %.*s\n", static_cast<int>(name.size()), name.data(), static_cast<int>(value.size()), value.data());
-}
-
-static void
-produce_layout(bool json)
-{
-  Layout::create();
-
-  RecProcessInit(RECM_STAND_ALONE, nullptr /* diags */);
-  LibRecordsConfigInit();
 
-  if (json) {
-    printf("{\n");
+  AppVersionInfo appVersionInfo;
+  appVersionInfo.setup(PACKAGE_NAME, "traffic_layout", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
+  // Process command line arguments and dump into variables
+  if (!process_args_ex(&appVersionInfo, argument_descriptions, countof(argument_descriptions), argv) ||
+      file_arguments[0] != nullptr) {
+    help_usage();
   }
-  print_var("PREFIX", Layout::get()->prefix, json);
-  print_var("BINDIR", RecConfigReadBinDir(), json);
-  print_var("SYSCONFDIR", RecConfigReadConfigDir(), json);
-  print_var("LIBDIR", Layout::get()->libdir, json);
-  print_var("LOGDIR", RecConfigReadLogDir(), json);
-  print_var("RUNTIMEDIR", RecConfigReadRuntimeDir(), json);
-  print_var("PLUGINDIR", RecConfigReadPluginDir(), json);
-  print_var("INCLUDEDIR", Layout::get()->includedir, json);
 
-  print_var("records.config", RecConfigReadConfigPath(nullptr, REC_CONFIG_FILE), json);
-  print_var("remap.config", RecConfigReadConfigPath("proxy.config.url_remap.filename"), json);
-  print_var("plugin.config", RecConfigReadConfigPath(nullptr, "plugin.config"), json);
-  print_var("ssl_multicert.config", RecConfigReadConfigPath("proxy.config.ssl.server.multicert.filename"), json);
-  print_var("ssl_server_name.config", RecConfigReadConfigPath("proxy.config.ssl.servername.filename"), json);
-  print_var("storage.config", RecConfigReadConfigPath("proxy.config.cache.storage_filename"), json);
-  print_var("hosting.config", RecConfigReadConfigPath("proxy.config.cache.hosting_filename"), json);
-  print_var("volume.config", RecConfigReadConfigPath("proxy.config.cache.volume_filename"), json);
-  print_var("ip_allow.config", RecConfigReadConfigPath("proxy.config.cache.ip_allow.filename"), json, true);
-  if (json) {
-    printf("}\n");
+  runroot_handler(argv, 0 != cl.json);
+
+  if (cl.features) {
+    produce_features(0 != cl.json);
+  } else {
+    produce_layout(0 != cl.json);
   }
+  return 0;
 }
 
-void
+// handle everything with runroot using engine
+int
 traffic_runroot(int argc, const char **argv)
 {
   // runroot engine for operations
   RunrootEngine engine;
   engine._argc = argc;
-  int i        = 0;
+
+  int i = 0;
   while (argv[i]) {
     engine._argv.push_back(argv[i]);
     ++i;
   }
-
   // parse the command line & put into global variable
-  engine.runroot_parse();
-
-  // check to clean the runroot or not
-  if (engine.clean_runroot())
-    return;
-
-  // start the runroot creating stuff
-  std::string original_root = TS_BUILD_PREFIX;
-
-  // setting up ts_runroot
-  // Use passed in parameter, else use ENV variable
-  std::string ts_runroot;
-  if (!engine.run_path.empty()) {
-    ts_runroot = engine.run_path;
-  } else {
-    if (getenv("TS_RUNROOT") != nullptr) {
-      ts_runroot = getenv("TS_RUNROOT");
-      ink_notice("Using TS_RUNROOT Env variable");
-    } else {
-      ink_fatal("Invalid ts_runroot path\n(please set command line path or Environment variable $TS_RUNROOT)");
-    }
+  if (!engine.runroot_parse()) {
+    engine.runroot_help_message(true, true);
+    return 0;
   }
+  // check sanity of the command about the runroot program
+  engine.sanity_check();
 
-  // handle the ts_runroot
-  // ts runroot must be an accessible path
-  std::ifstream check_file(Layout::relative_to(ts_runroot, "runroot_path.yml"));
-  if (check_file.good()) {
-    // if the path already ts_runroot, use it
-    ink_notice("Using existing TS_RUNROOT...");
-    ink_notice("Please remove the old TS_RUNROOT if new runroot is needed \n(usage: traffic_runroot rm /path/...)");
-    return;
-  } else if (exists(ts_runroot) && is_directory(ts_runroot)) {
-    ink_fatal("directory already exist");
-  }
-
-  // create new root & copy from original to new runroot. then fill in the map
-  engine.copy_runroot(original_root, ts_runroot);
-
-  // create and emit to yaml file the key value pairs of path
-  std::ofstream yamlfile;
-  std::string yaml_path = Layout::relative_to(ts_runroot, "runroot_path.yml");
-  yamlfile.open(yaml_path);
+  // create layout for runroot handling
+  runroot_handler(argv);
+  Layout::create();
 
-  for (auto it : engine.path_map) {
-    // out put key value pairs of path
-    yamlfile << it.first << ": " << it.second << std::endl;
+  // check to clean the runroot or not
+  if (engine.clean_flag) {
+    engine.clean_runroot();
+    std::cout << "runroot removed" << std::endl;
+  } else {
+    engine.create_runroot();
   }
-  ink_notice("TS runroot initialized");
-
-  return;
+  return 0;
 }
 
 int
 main(int argc, const char **argv)
 {
-  // check for traffic_runroot operations
-  for (int i = 0; i < argc; i++) {
-    if (!strcmp(argv[i], "--init") || !strcmp(argv[i], "--remove") || !strcmp(argv[i], "-i") || !strcmp(argv[i], "-r") ||
-        !strcmp(argv[i], "--force")) {
-      traffic_runroot(argc, argv);
-      exit(0);
+  const subcommand commands[] = {
+    {info, "info", "Show the layout"},
+    {traffic_runroot, "init", "Initialize the ts_runroot sandbox"},
+    {traffic_runroot, "remove", "Remove the ts_runroot sandbox"},
+  };
+
+  // with command (info, init, remove)
+  for (unsigned i = 0; i < countof(commands); ++i) {
+    if (!argv[1])
+      break;
+    if (strcmp(argv[1], commands[i].name.c_str()) == 0) {
+      return commands[i].handler(argc, argv);
     }
   }
-  // normal print out layout operation
-  AppVersionInfo appVersionInfo;
-
-  appVersionInfo.setup(PACKAGE_NAME, "traffic_layout", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
-
-  // Process command line arguments and dump into variables
-  process_args(&appVersionInfo, argument_descriptions, countof(argument_descriptions), argv);
 
-  runroot_handler(argv, 0 != cl.json);
-
-  if (cl.features) {
-    produce_features(0 != cl.json);
-  } else {
-    produce_layout(0 != cl.json);
-  }
-  exit(0);
+  // without command (info, init, remove), default behavior
+  return info(argc, argv);
 }
diff --git a/doc/appendices/command-line/traffic_layout.en.rst b/doc/appendices/command-line/traffic_layout.en.rst
index d013498..f4b52b1 100644
--- a/doc/appendices/command-line/traffic_layout.en.rst
+++ b/doc/appendices/command-line/traffic_layout.en.rst
@@ -33,51 +33,58 @@ is for the setup of traffic server runroot. It will create a runtime sandbox for
 traffic server to run under.
 
 #. Use :program:`traffic_layout` to create sandbox.
-#. Run any program use the sandbox with ``--run-root=/path/to/file`` or ``--run-root``.
+#. Run any program using the sandbox with ``--run-root=/path/to/file`` or ``--run-root``.
 
 How it works:
 --------------
 
-#. Create a sandbox directory for programs to run under
-#. Copy and symlink build time directories and files to sandbox, allowing users to modify freely.
-#. Emit a yaml file that defines layout structure for other programs to use.
+#. Create a sandbox directory for programs to run under.
+#. Copy and symlink build time directories and files to the sandbox, allowing users to modify freely.
+#. Emit a yaml file that defines layout structure for other programs to use (relative path).
 
 Options:
 =============
 
 #. Initialize the runroot: ::
 
-      traffic_layout --init /path/to/sandbox/
+      traffic_layout init (--path /path/to/sandbox/)
 
-   If no path is found, it will find :envvar:`TS_RUNROOT`.
+   Use the current working directory or specific path to create runroot.
 
 #. Remove the runroot: ::
 
-      traffic_layout --remove /path/to/sandbox/
+      traffic_layout remove --path /path/to/sandbox/
 
    Remove the sandbox we created(check yaml file).
-   If no path provided, it will find :envvar:`TS_RUNROOT`.
-   If :envvar:`TS_RUNROOT` not found, it will find bin executing path & current working directory.
+   If no path provided, it will check bin executing path and current working directory to clean.
 
-#. Force flag for creating: ::
+#. Use Force flag for creating: ::
 
-      traffic_runroot --force --init /path/to/sandbox
+      traffic_layout init --force (--path /path/to/sandbox)
+      traffic_layout remove --force --path /path/to/sandbox
 
-   Force create sandbox and overwrite existing directory
+   Force create sandbox and overwrite existing directory when directory is not empty or has a yaml file in it.
+   Force removing a directory when directory has no yaml file.
+
+#. Use absolute flag for creating: ::
+
+      traffic_layout init --absolute (--path /path/to/sandbox)
+
+   create sandbox and put directories in the yaml file with absolute path form.
 
 Usage for other programs:
 ==============================================
 
+Use command line path or use :envvar:`TS_RUNROOT`.
+If command line path and envvar are not found, program will try to find the current executing program bin path or current working directory to use as runroot if the yaml file is found. 
+For bin path and cwd, it can go one level up to the parent directory to find the yaml file. ::
 
-Use pass in path or use :envvar:`TS_RUNROOT`.
-If both not found, program will try to find bin path & current working directory. ::
-
-   trafficserver --run-root=/path/to/runroot
-   trafficserver --run-root
+   trafficserver --run-root=/path/to/runroot (use /path/to/runroot as runroot)
+   trafficserver --run-root                  (use $TS_RUNROOT as runroot)
 
 .. envvar:: TS_RUNROOT
 
-   Path to run root file.
+   The path to run root directory.
 
 Notes
 ==========
diff --git a/lib/ts/I_Layout.h b/lib/ts/I_Layout.h
index b742340..5716910 100644
--- a/lib/ts/I_Layout.h
+++ b/lib/ts/I_Layout.h
@@ -44,12 +44,6 @@ struct Layout {
   ~Layout();
 
   /**
-   return use runroot or not
-
-  */
-  bool check_runroot();
-
-  /**
    Return file path relative to Layout->prefix
 
   */
@@ -103,7 +97,6 @@ struct Layout {
   std::string runtimedir;
   std::string logdir;
   std::string mandir;
-  std::string infodir;
   std::string cachedir;
 };
 
diff --git a/lib/ts/Layout.cc b/lib/ts/Layout.cc
index 626fca8..1ee124d 100644
--- a/lib/ts/Layout.cc
+++ b/lib/ts/Layout.cc
@@ -27,8 +27,8 @@
 #include "ts/ink_memory.h"
 #include "ts/ink_string.h"
 #include "ts/I_Layout.h"
+#include "ts/runroot.h"
 
-#include <fstream>
 #include <unordered_map>
 
 static Layout *layout = nullptr;
@@ -111,71 +111,6 @@ Layout::relative_to(char *buf, size_t bufsz, ts::string_view dir, ts::string_vie
   }
 }
 
-bool
-Layout::check_runroot()
-{
-  if (getenv("USING_RUNROOT") == nullptr) {
-    return false;
-  }
-
-  std::string env_path = getenv("USING_RUNROOT");
-  int len              = env_path.size();
-  if ((len + 1) > PATH_NAME_MAX) {
-    ink_fatal("TS_RUNROOT environment variable is too big: %d, max %d\n", len, PATH_NAME_MAX - 1);
-  }
-  std::ifstream file;
-  std::string yaml_path = layout_relative(env_path, "runroot_path.yml");
-
-  file.open(yaml_path);
-  if (!file.good()) {
-    ink_warning("Bad env path, continue with default value");
-    return false;
-  }
-
-  std::ifstream yamlfile(yaml_path);
-  std::unordered_map<std::string, std::string> runroot_map;
-  std::string str;
-  while (std::getline(yamlfile, str)) {
-    int pos = str.find(':');
-    runroot_map[str.substr(0, pos)] = str.substr(pos + 2);
-  }
-
-  prefix        = runroot_map["prefix"];
-  exec_prefix   = runroot_map["exec_prefix"];
-  bindir        = runroot_map["bindir"];
-  sbindir       = runroot_map["sbindir"];
-  sysconfdir    = runroot_map["sysconfdir"];
-  datadir       = runroot_map["datadir"];
-  includedir    = runroot_map["includedir"];
-  libdir        = runroot_map["libdir"];
-  libexecdir    = runroot_map["libexecdir"];
-  localstatedir = runroot_map["localstatedir"];
-  runtimedir    = runroot_map["runtimedir"];
-  logdir        = runroot_map["logdir"];
-  mandir        = runroot_map["mandir"];
-  infodir       = runroot_map["infodir"];
-  cachedir      = runroot_map["cachedir"];
-
-  // // for yaml lib operations
-  // YAML::Node yamlfile = YAML::LoadFile(yaml_path);
-  // prefix              = yamlfile["prefix"].as<string>();
-  // exec_prefix         = yamlfile["exec_prefix"].as<string>();
-  // bindir              = yamlfile["bindir"].as<string>();
-  // sbindir             = yamlfile["sbindir"].as<string>();
-  // sysconfdir          = yamlfile["sysconfdir"].as<string>();
-  // datadir             = yamlfile["datadir"].as<string>();
-  // includedir          = yamlfile["includedir"].as<string>();
-  // libdir              = yamlfile["libdir"].as<string>();
-  // libexecdir          = yamlfile["libexecdir"].as<string>();
-  // localstatedir       = yamlfile["localstatedir"].as<string>();
-  // runtimedir          = yamlfile["runtimedir"].as<string>();
-  // logdir              = yamlfile["logdir"].as<string>();
-  // mandir              = yamlfile["mandir"].as<string>();
-  // infodir             = yamlfile["infodir"].as<string>();
-  // cachedir            = yamlfile["cachedir"].as<string>();
-  return true;
-}
-
 Layout::Layout(ts::string_view const _prefix)
 {
   if (!_prefix.empty()) {
@@ -183,7 +118,22 @@ Layout::Layout(ts::string_view const _prefix)
   } else {
     std::string path;
     int len;
-    if (check_runroot()) {
+    std::unordered_map<std::string, std::string> dir_map = check_runroot();
+    if (dir_map.size() != 0) {
+      prefix        = dir_map["prefix"];
+      exec_prefix   = dir_map["exec_prefix"];
+      bindir        = dir_map["bindir"];
+      sbindir       = dir_map["sbindir"];
+      sysconfdir    = dir_map["sysconfdir"];
+      datadir       = dir_map["datadir"];
+      includedir    = dir_map["includedir"];
+      libdir        = dir_map["libdir"];
+      libexecdir    = dir_map["libexecdir"];
+      localstatedir = dir_map["localstatedir"];
+      runtimedir    = dir_map["runtimedir"];
+      logdir        = dir_map["logdir"];
+      mandir        = dir_map["mandir"];
+      cachedir      = dir_map["cachedir"];
       return;
     }
     if (getenv("TS_ROOT") != nullptr) {
@@ -214,7 +164,6 @@ Layout::Layout(ts::string_view const _prefix)
   runtimedir    = layout_relative(prefix, TS_BUILD_RUNTIMEDIR);
   logdir        = layout_relative(prefix, TS_BUILD_LOGDIR);
   mandir        = layout_relative(prefix, TS_BUILD_MANDIR);
-  infodir       = layout_relative(prefix, TS_BUILD_INFODIR);
   cachedir      = layout_relative(prefix, TS_BUILD_CACHEDIR);
 }
 
diff --git a/lib/ts/runroot.cc b/lib/ts/runroot.cc
index 18ab141..15c040a 100644
--- a/lib/ts/runroot.cc
+++ b/lib/ts/runroot.cc
@@ -34,7 +34,7 @@ Need a yaml file in the sandbox with key value pairs of all directory locations
 
 Directories needed in the yaml file:
 prefix, exec_prefix, includedir, localstatedir, bindir, logdir, mandir, sbindir, sysconfdir,
-datadir, libexecdir, libdir, runtimedir, infodir, cachedir.
+datadir, libexecdir, libdir, runtimedir, cachedir.
 */
 
 #include "ts/ink_error.h"
@@ -46,6 +46,8 @@ datadir, libexecdir, libdir, runtimedir, infodir, cachedir.
 #include <set>
 #include <unistd.h>
 
+static std::string using_runroot = {};
+
 // the function for the checking of the yaml file in parent path
 // if found return the parent path containing the yaml file
 std::string
@@ -106,7 +108,7 @@ runroot_handler(const char **argv, bool json)
     if (yaml_checkfile.good()) {
       if (!json)
         ink_notice("using command line path as RUNROOT");
-      setenv("USING_RUNROOT", path.c_str(), true);
+      using_runroot = path;
       return;
     } else {
       if (!json)
@@ -116,22 +118,70 @@ runroot_handler(const char **argv, bool json)
   // 2. argv provided invalid/no yaml file, then check env variable
   char *env_val = getenv("TS_RUNROOT");
   if ((env_val != nullptr) && is_directory(env_val)) {
-    setenv("USING_RUNROOT", env_val, true);
+    using_runroot = env_val;
     if (!json)
       ink_notice("using the environment variable TS_RUNROOT");
     return;
   }
   // 3. find parent path of bin/pwd to check
-  char cwd[MAX_CWD_LEN]      = {0};
+  char cwd[PATH_MAX]         = {0};
   char RealBinPath[PATH_MAX] = {0};
   if ((argv[0] != nullptr) && (getcwd(cwd, sizeof(cwd)) != nullptr) && (realpath(argv[0], RealBinPath) != nullptr)) {
     std::vector<std::string> TwoPath = {RealBinPath, cwd};
     for (auto it : TwoPath) {
       std::string path = check_parent_path(it);
       if (!path.empty()) {
-        setenv("USING_RUNROOT", path.c_str(), true);
+        using_runroot = path;
         return;
       }
     }
   }
 }
+
+// return a map of all path in runroot_path.yml
+std::unordered_map<std::string, std::string>
+runroot_map(std::string &yaml_path, std::string &prefix)
+{
+  std::ifstream file;
+  file.open(yaml_path);
+  if (!file.good()) {
+    ink_warning("Bad env path, continue with default value");
+    return std::unordered_map<std::string, std::string>{};
+  }
+
+  std::ifstream yamlfile(yaml_path);
+  std::unordered_map<std::string, std::string> runroot_map;
+  std::string str;
+  while (std::getline(yamlfile, str)) {
+    int pos = str.find(':');
+    runroot_map[str.substr(0, pos)] = str.substr(pos + 2);
+  }
+
+  // change it to absolute path in the map
+  for (auto it : runroot_map) {
+    if (it.second[0] != '/') {
+      runroot_map[it.first] = Layout::relative_to(prefix, it.second);
+    }
+  }
+  return runroot_map;
+}
+
+// check for the using of runroot
+// a map of all path will be returned
+// if we do not use runroot, a empty map will be returned.
+std::unordered_map<std::string, std::string>
+check_runroot()
+{
+  if (using_runroot.empty()) {
+    return std::unordered_map<std::string, std::string>{};
+  }
+
+  std::string env_path = using_runroot;
+  int len              = env_path.size();
+  if ((len + 1) > PATH_NAME_MAX) {
+    ink_fatal("TS_RUNROOT environment variable is too big: %d, max %d\n", len, PATH_NAME_MAX - 1);
+  }
+  std::string yaml_path = Layout::relative_to(env_path, "runroot_path.yml");
+
+  return runroot_map(yaml_path, env_path);
+}
diff --git a/lib/ts/runroot.h b/lib/ts/runroot.h
index ee0118b..78bdac4 100644
--- a/lib/ts/runroot.h
+++ b/lib/ts/runroot.h
@@ -27,9 +27,14 @@
 #pragma once
 
 #include <string>
-
-#define MAX_CWD_LEN 1024
+#include <unordered_map>
 
 std::string check_parent_path(const std::string &path, bool json = false);
 
 void runroot_handler(const char **argv, bool json = false);
+
+// get runroot map from yaml path and prefix
+std::unordered_map<std::string, std::string> runroot_map(std::string &yaml_path, std::string &prefix);
+
+// help check runroot for layout
+std::unordered_map<std::string, std::string> check_runroot();
diff --git a/tests/gold_tests/basic/runroot-layout.test.py b/tests/gold_tests/basic/runroot-layout.test.py
index 0394aca..2d8f37d 100644
--- a/tests/gold_tests/basic/runroot-layout.test.py
+++ b/tests/gold_tests/basic/runroot-layout.test.py
@@ -30,7 +30,7 @@ path = os.path.join(p.Env['TS_ROOT'], "runroot")
 
 # normal init from pass in path
 tr = Test.AddTestRun("Test traffic_layout init")
-tr.Processes.Default.Command = "$ATS_BIN/traffic_layout --init " + path
+tr.Processes.Default.Command = "$ATS_BIN/traffic_layout init --path " + path
 tr.Processes.Default.ReturnCode = 0
 d = tr.Disk.Directory(path)
 d.Exists = True
@@ -39,31 +39,31 @@ f.Exists = True
 
 # remove from pass in path
 tr = Test.AddTestRun("Test traffoc_layout remove")
-tr.Processes.Default.Command = "$ATS_BIN/traffic_layout --remove " + path
+tr.Processes.Default.Command = "$ATS_BIN/traffic_layout remove --path " + path
 tr.Processes.Default.ReturnCode = 0
 d = tr.Disk.Directory(path)
 d.Exists = False
 f = tr.Disk.File(os.path.join(path, "runroot_path.yml"))
 f.Exists = False
 
-path += '/'
+# path += '/'
 
-#use env variable to init
-tr = Test.AddTestRun("Test traffic_layout ENV init")
-tr.Processes.Default.Env["TS_RUNROOT"] = path
-tr.Processes.Default.Command = "$ATS_BIN/traffic_layout --init"
-tr.Processes.Default.ReturnCode = 0
-d = tr.Disk.Directory(path)
-d.Exists = True
-f = tr.Disk.File(os.path.join(path, "runroot_path.yml"))
-f.Exists = True
+# #use env variable to init
+# tr = Test.AddTestRun("Test traffic_layout ENV init")
+# tr.Processes.Default.Env["TS_RUNROOT"] = path
+# tr.Processes.Default.Command = "$ATS_BIN/traffic_layout init"
+# tr.Processes.Default.ReturnCode = 0
+# d = tr.Disk.Directory(path)
+# d.Exists = True
+# f = tr.Disk.File(os.path.join(path, "runroot_path.yml"))
+# f.Exists = True
 
-#use env variable to remove
-tr = Test.AddTestRun("Test traffic_layout ENV remove")
-tr.Processes.Default.Env["TS_RUNROOT"] = path
-tr.Processes.Default.Command = "$ATS_BIN/traffic_layout --remove"
-tr.Processes.Default.ReturnCode = 0
-d = tr.Disk.Directory(path)
-d.Exists = False
-f = tr.Disk.File(os.path.join(path, "runroot_path.yml"))
-f.Exists = False
\ No newline at end of file
+# #use env variable to remove
+# tr = Test.AddTestRun("Test traffic_layout ENV remove")
+# tr.Processes.Default.Env["TS_RUNROOT"] = path
+# tr.Processes.Default.Command = "$ATS_BIN/traffic_layout remove"
+# tr.Processes.Default.ReturnCode = 0
+# d = tr.Disk.Directory(path)
+# d.Exists = False
+# f = tr.Disk.File(os.path.join(path, "runroot_path.yml"))
+# f.Exists = False

-- 
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].