1 // Copyright (c) 2010 The NetBSD Foundation, Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions 6 // are met: 7 // 1. Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright 10 // notice, this list of conditions and the following disclaimer in the 11 // documentation and/or other materials provided with the distribution. 12 // 13 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 14 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 15 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 18 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 20 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 22 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 extern "C" { 27 #include <unistd.h> 28 } 29 30 #include <cerrno> 31 #include <cstdlib> 32 #include <cstring> 33 #include <iostream> 34 #include <utility> 35 #include <vector> 36 37 #include "atf-c++/detail/application.hpp" 38 #include "atf-c++/detail/env.hpp" 39 #include "atf-c++/detail/fs.hpp" 40 #include "atf-c++/detail/sanity.hpp" 41 42 // ------------------------------------------------------------------------ 43 // Auxiliary functions. 44 // ------------------------------------------------------------------------ 45 46 namespace { 47 48 static 49 std::string 50 fix_plain_name(const char *filename) 51 { 52 const atf::fs::path filepath(filename); 53 if (filepath.branch_path().str() == ".") 54 return std::string("./") + filename; 55 else 56 return std::string(filename); 57 } 58 59 static 60 std::string 61 construct_script(const char* filename) 62 { 63 const std::string libexecdir = atf::env::get( 64 "ATF_LIBEXECDIR", ATF_LIBEXECDIR); 65 const std::string pkgdatadir = atf::env::get( 66 "ATF_PKGDATADIR", ATF_PKGDATADIR); 67 const std::string shell = atf::env::get("ATF_SHELL", ATF_SHELL); 68 69 std::string command; 70 71 command += ("Atf_Check='" + libexecdir + "/atf-check' ; " + 72 "Atf_Shell='" + shell + "' ; " + 73 ". " + pkgdatadir + "/libatf-sh.subr ; " + 74 ". " + fix_plain_name(filename) + " ; " + 75 "main \"${@}\""); 76 return command; 77 } 78 79 static 80 std::vector<std::string> 81 construct_argv(const std::string& shell, const int interpreter_argc, 82 const char* const* interpreter_argv) 83 { 84 PRE(interpreter_argc >= 1); 85 PRE(interpreter_argv[0] != NULL); 86 87 std::string script = construct_script(interpreter_argv[0]); 88 89 std::vector<std::string> argv; 90 91 argv.push_back(shell); 92 argv.push_back(std::string("-c")); 93 argv.push_back(std::move(script)); 94 argv.push_back(std::string(interpreter_argv[0])); 95 96 for (int i = 1; i < interpreter_argc; i++) 97 argv.push_back(interpreter_argv[i]); 98 99 return argv; 100 } 101 102 } // anonymous namespace 103 104 // ------------------------------------------------------------------------ 105 // The "atf_sh" class. 106 // ------------------------------------------------------------------------ 107 108 class atf_sh : public atf::application::app { 109 static const char* m_description; 110 111 atf::fs::path m_shell; 112 113 options_set specific_options(void) const; 114 void process_option(int, const char*); 115 116 public: 117 atf_sh(void); 118 119 int main(void); 120 }; 121 122 const char* atf_sh::m_description = 123 "atf-sh is a shell interpreter that extends the functionality of the " 124 "system sh(1) with the atf-sh library."; 125 126 atf_sh::atf_sh(void) : 127 app(m_description, "atf-sh(1)"), 128 m_shell(atf::fs::path(atf::env::get("ATF_SHELL", ATF_SHELL))) 129 { 130 } 131 132 atf_sh::options_set 133 atf_sh::specific_options(void) 134 const 135 { 136 using atf::application::option; 137 options_set opts; 138 139 INV(m_shell == atf::fs::path(atf::env::get("ATF_SHELL", ATF_SHELL))); 140 opts.insert(option('s', "shell", "Path to the shell interpreter to use; " 141 "default: " + m_shell.str())); 142 143 return opts; 144 } 145 146 void 147 atf_sh::process_option(int ch, const char* arg) 148 { 149 switch (ch) { 150 case 's': 151 m_shell = atf::fs::path(arg); 152 break; 153 154 default: 155 UNREACHABLE; 156 } 157 } 158 159 int 160 atf_sh::main(void) 161 { 162 if (m_argc < 1) 163 throw atf::application::usage_error("No test program provided"); 164 165 const atf::fs::path script(m_argv[0]); 166 if (!atf::fs::exists(script)) 167 throw std::runtime_error("The test program '" + script.str() + "' " 168 "does not exist"); 169 170 std::vector<std::string> argvv = construct_argv(m_shell.str(), m_argc, 171 m_argv); 172 std::vector<const char *> argv; 173 174 for (std::size_t i = 0; i < argvv.size(); i++) 175 argv.push_back(argvv[i].c_str()); 176 argv.push_back(NULL); 177 178 const int ret = execv(m_shell.c_str(), 179 const_cast<char* const *>(argv.data())); 180 INV(ret == -1); 181 182 std::cerr << "Failed to execute " << m_shell.str() << ": " 183 << std::strerror(errno) << "\n"; 184 return EXIT_FAILURE; 185 } 186 187 int 188 main(int argc, char* const* argv) 189 { 190 return atf_sh().run(argc, argv); 191 } 192