atf-sh.cpp revision 275988
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
26extern "C" {
27#include <unistd.h>
28}
29
30#include <cerrno>
31#include <cstdlib>
32#include <cstring>
33#include <iostream>
34
35#include "atf-c++/detail/application.hpp"
36#include "atf-c++/detail/env.hpp"
37#include "atf-c++/detail/fs.hpp"
38#include "atf-c++/detail/sanity.hpp"
39
40// ------------------------------------------------------------------------
41// Auxiliary functions.
42// ------------------------------------------------------------------------
43
44namespace {
45
46static
47std::string
48fix_plain_name(const char *filename)
49{
50    const atf::fs::path filepath(filename);
51    if (filepath.branch_path().str() == ".")
52        return std::string("./") + filename;
53    else
54        return std::string(filename);
55}
56
57static
58std::string*
59construct_script(const char* filename)
60{
61    const std::string libexecdir = atf::env::get(
62        "ATF_LIBEXECDIR", ATF_LIBEXECDIR);
63    const std::string pkgdatadir = atf::env::get(
64        "ATF_PKGDATADIR", ATF_PKGDATADIR);
65    const std::string shell = atf::env::get("ATF_SHELL", ATF_SHELL);
66
67    std::string* command = new std::string();
68    command->reserve(512);
69    (*command) += ("Atf_Check='" + libexecdir + "/atf-check' ; " +
70                   "Atf_Shell='" + shell + "' ; " +
71                   ". " + pkgdatadir + "/libatf-sh.subr ; " +
72                   ". " + fix_plain_name(filename) + " ; " +
73                   "main \"${@}\"");
74    return command;
75}
76
77static
78const char**
79construct_argv(const std::string& shell, const int interpreter_argc,
80               const char* const* interpreter_argv)
81{
82    PRE(interpreter_argc >= 1);
83    PRE(interpreter_argv[0] != NULL);
84
85    const std::string* script = construct_script(interpreter_argv[0]);
86
87    const int count = 4 + (interpreter_argc - 1) + 1;
88    const char** argv = new const char*[count];
89    argv[0] = shell.c_str();
90    argv[1] = "-c";
91    argv[2] = script->c_str();
92    argv[3] = interpreter_argv[0];
93
94    for (int i = 1; i < interpreter_argc; i++)
95        argv[4 + i - 1] = interpreter_argv[i];
96
97    argv[count - 1] = NULL;
98
99    return argv;
100}
101
102} // anonymous namespace
103
104// ------------------------------------------------------------------------
105// The "atf_sh" class.
106// ------------------------------------------------------------------------
107
108class 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
116public:
117    atf_sh(void);
118
119    int main(void);
120};
121
122const 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
126atf_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
132atf_sh::options_set
133atf_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
146void
147atf_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
159int
160atf_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    const char** argv = construct_argv(m_shell.str(), m_argc, m_argv);
171    // Don't bother keeping track of the memory allocated by construct_argv:
172    // we are going to exec or die immediately.
173
174    const int ret = execv(m_shell.c_str(), const_cast< char** >(argv));
175    INV(ret == -1);
176    std::cerr << "Failed to execute " << m_shell.str() << ": "
177              << std::strerror(errno) << "\n";
178    return EXIT_FAILURE;
179}
180
181int
182main(int argc, char* const* argv)
183{
184    return atf_sh().run(argc, argv);
185}
186