1*11be35a1SLionel Sambuc // Copyright 2012 Google Inc.
2*11be35a1SLionel Sambuc // All rights reserved.
3*11be35a1SLionel Sambuc //
4*11be35a1SLionel Sambuc // Redistribution and use in source and binary forms, with or without
5*11be35a1SLionel Sambuc // modification, are permitted provided that the following conditions are
6*11be35a1SLionel Sambuc // met:
7*11be35a1SLionel Sambuc //
8*11be35a1SLionel Sambuc // * Redistributions of source code must retain the above copyright
9*11be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer.
10*11be35a1SLionel Sambuc // * Redistributions in binary form must reproduce the above copyright
11*11be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer in the
12*11be35a1SLionel Sambuc // documentation and/or other materials provided with the distribution.
13*11be35a1SLionel Sambuc // * Neither the name of Google Inc. nor the names of its contributors
14*11be35a1SLionel Sambuc // may be used to endorse or promote products derived from this software
15*11be35a1SLionel Sambuc // without specific prior written permission.
16*11be35a1SLionel Sambuc //
17*11be35a1SLionel Sambuc // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*11be35a1SLionel Sambuc // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*11be35a1SLionel Sambuc // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*11be35a1SLionel Sambuc // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*11be35a1SLionel Sambuc // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*11be35a1SLionel Sambuc // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*11be35a1SLionel Sambuc // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*11be35a1SLionel Sambuc // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*11be35a1SLionel Sambuc // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*11be35a1SLionel Sambuc // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*11be35a1SLionel Sambuc // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*11be35a1SLionel Sambuc
29*11be35a1SLionel Sambuc #if defined(HAVE_CONFIG_H)
30*11be35a1SLionel Sambuc #include "config.h"
31*11be35a1SLionel Sambuc #endif
32*11be35a1SLionel Sambuc
33*11be35a1SLionel Sambuc #include "cli.h"
34*11be35a1SLionel Sambuc
35*11be35a1SLionel Sambuc #include <assert.h>
36*11be35a1SLionel Sambuc #include <err.h>
37*11be35a1SLionel Sambuc #include <errno.h>
38*11be35a1SLionel Sambuc #include <limits.h>
39*11be35a1SLionel Sambuc #include <stdbool.h>
40*11be35a1SLionel Sambuc #include <stdlib.h>
41*11be35a1SLionel Sambuc #include <string.h>
42*11be35a1SLionel Sambuc #include <unistd.h>
43*11be35a1SLionel Sambuc
44*11be35a1SLionel Sambuc #include "defs.h"
45*11be35a1SLionel Sambuc #include "error.h"
46*11be35a1SLionel Sambuc #include "run.h"
47*11be35a1SLionel Sambuc
48*11be35a1SLionel Sambuc #if !defined(GID_MAX)
49*11be35a1SLionel Sambuc # define GID_MAX INT_MAX
50*11be35a1SLionel Sambuc #endif
51*11be35a1SLionel Sambuc #if !defined(UID_MAX)
52*11be35a1SLionel Sambuc # define UID_MAX INT_MAX
53*11be35a1SLionel Sambuc #endif
54*11be35a1SLionel Sambuc
55*11be35a1SLionel Sambuc #if defined(HAVE_GETOPT_GNU)
56*11be35a1SLionel Sambuc # define GETOPT_PLUS "+"
57*11be35a1SLionel Sambuc #else
58*11be35a1SLionel Sambuc # define GETOPT_PLUS
59*11be35a1SLionel Sambuc #endif
60*11be35a1SLionel Sambuc
61*11be35a1SLionel Sambuc
62*11be35a1SLionel Sambuc /// Terminates execution if the given error is set.
63*11be35a1SLionel Sambuc ///
64*11be35a1SLionel Sambuc /// \param error The error to validate.
65*11be35a1SLionel Sambuc ///
66*11be35a1SLionel Sambuc /// \post The program terminates if the given error is set.
67*11be35a1SLionel Sambuc static void
check_error(kyua_error_t error)68*11be35a1SLionel Sambuc check_error(kyua_error_t error)
69*11be35a1SLionel Sambuc {
70*11be35a1SLionel Sambuc if (kyua_error_is_set(error)) {
71*11be35a1SLionel Sambuc const bool usage_error = kyua_error_is_type(error,
72*11be35a1SLionel Sambuc kyua_usage_error_type);
73*11be35a1SLionel Sambuc
74*11be35a1SLionel Sambuc char buffer[1024];
75*11be35a1SLionel Sambuc kyua_error_format(error, buffer, sizeof(buffer));
76*11be35a1SLionel Sambuc kyua_error_free(error);
77*11be35a1SLionel Sambuc
78*11be35a1SLionel Sambuc errx(usage_error ? EXIT_USAGE_ERROR : EXIT_INTERNAL_ERROR,
79*11be35a1SLionel Sambuc "%s", buffer);
80*11be35a1SLionel Sambuc }
81*11be35a1SLionel Sambuc }
82*11be35a1SLionel Sambuc
83*11be35a1SLionel Sambuc
84*11be35a1SLionel Sambuc /// Converts a string to an unsigned long.
85*11be35a1SLionel Sambuc ///
86*11be35a1SLionel Sambuc /// \param str The string to convert.
87*11be35a1SLionel Sambuc /// \param message Part of the error message to print if the string does not
88*11be35a1SLionel Sambuc /// represent a valid unsigned long number.
89*11be35a1SLionel Sambuc /// \param max Maximum accepted value.
90*11be35a1SLionel Sambuc ///
91*11be35a1SLionel Sambuc /// \return The converted numerical value.
92*11be35a1SLionel Sambuc ///
93*11be35a1SLionel Sambuc /// \post The program terminates if the value is invalid.
94*11be35a1SLionel Sambuc static unsigned long
parse_ulong(const char * str,const char * message,const unsigned long max)95*11be35a1SLionel Sambuc parse_ulong(const char* str, const char* message, const unsigned long max)
96*11be35a1SLionel Sambuc {
97*11be35a1SLionel Sambuc char *endptr;
98*11be35a1SLionel Sambuc
99*11be35a1SLionel Sambuc errno = 0;
100*11be35a1SLionel Sambuc const unsigned long value = strtoul(str, &endptr, 10);
101*11be35a1SLionel Sambuc if (str[0] == '\0' || *endptr != '\0')
102*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "%s '%s' (not a number)", message, str);
103*11be35a1SLionel Sambuc else if (errno == ERANGE || value == LONG_MAX || value > max)
104*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "%s '%s' (out of range)", message, str);
105*11be35a1SLionel Sambuc return value;
106*11be35a1SLionel Sambuc }
107*11be35a1SLionel Sambuc
108*11be35a1SLionel Sambuc
109*11be35a1SLionel Sambuc /// Clears getopt(3) state to allow calling the function again.
110*11be35a1SLionel Sambuc static void
reset_getopt(void)111*11be35a1SLionel Sambuc reset_getopt(void)
112*11be35a1SLionel Sambuc {
113*11be35a1SLionel Sambuc opterr = 0;
114*11be35a1SLionel Sambuc optind = GETOPT_OPTIND_RESET_VALUE;
115*11be35a1SLionel Sambuc #if defined(HAVE_GETOPT_WITH_OPTRESET)
116*11be35a1SLionel Sambuc optreset = 1;
117*11be35a1SLionel Sambuc #endif
118*11be35a1SLionel Sambuc }
119*11be35a1SLionel Sambuc
120*11be35a1SLionel Sambuc
121*11be35a1SLionel Sambuc /// Prints the list of test cases and their metadata in a test program.
122*11be35a1SLionel Sambuc ///
123*11be35a1SLionel Sambuc /// \param argc Number of arguments to the command, including the command name.
124*11be35a1SLionel Sambuc /// \param argv Arguments to the command, including the command name.
125*11be35a1SLionel Sambuc /// \param tester Description of the tester implemented by this binary.
126*11be35a1SLionel Sambuc /// \param run_params Execution parameters to configure the test process.
127*11be35a1SLionel Sambuc ///
128*11be35a1SLionel Sambuc /// \return An exit status to indicate the success or failure of the listing.
129*11be35a1SLionel Sambuc ///
130*11be35a1SLionel Sambuc /// \post Usage errors terminate the execution of the program right away.
131*11be35a1SLionel Sambuc static int
list_command(const int argc,char * const * const argv,const kyua_cli_tester_t * tester,const kyua_run_params_t * run_params)132*11be35a1SLionel Sambuc list_command(const int argc, char* const* const argv,
133*11be35a1SLionel Sambuc const kyua_cli_tester_t* tester,
134*11be35a1SLionel Sambuc const kyua_run_params_t* run_params)
135*11be35a1SLionel Sambuc {
136*11be35a1SLionel Sambuc if (argc < 2)
137*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "No test program provided");
138*11be35a1SLionel Sambuc else if (argc > 2)
139*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "Only one test program allowed");
140*11be35a1SLionel Sambuc const char* test_program = argv[1];
141*11be35a1SLionel Sambuc
142*11be35a1SLionel Sambuc check_error(tester->list_test_cases(test_program, run_params));
143*11be35a1SLionel Sambuc return EXIT_SUCCESS;
144*11be35a1SLionel Sambuc }
145*11be35a1SLionel Sambuc
146*11be35a1SLionel Sambuc
147*11be35a1SLionel Sambuc /// Runs and cleans up a single test case.
148*11be35a1SLionel Sambuc ///
149*11be35a1SLionel Sambuc /// \param argc Number of arguments to the command, including the command name.
150*11be35a1SLionel Sambuc /// \param argv Arguments to the command, including the command name.
151*11be35a1SLionel Sambuc /// \param tester Description of the tester implemented by this binary.
152*11be35a1SLionel Sambuc /// \param run_params Execution parameters to configure the test process.
153*11be35a1SLionel Sambuc ///
154*11be35a1SLionel Sambuc /// \return An exit status to indicate the success or failure of the test case
155*11be35a1SLionel Sambuc /// execution.
156*11be35a1SLionel Sambuc ///
157*11be35a1SLionel Sambuc /// \post Usage errors terminate the execution of the program right away.
158*11be35a1SLionel Sambuc static int
test_command(int argc,char * const * argv,const kyua_cli_tester_t * tester,const kyua_run_params_t * run_params)159*11be35a1SLionel Sambuc test_command(int argc, char* const* argv, const kyua_cli_tester_t* tester,
160*11be35a1SLionel Sambuc const kyua_run_params_t* run_params)
161*11be35a1SLionel Sambuc {
162*11be35a1SLionel Sambuc # define MAX_USER_VARIABLES 256
163*11be35a1SLionel Sambuc const char* user_variables[MAX_USER_VARIABLES];
164*11be35a1SLionel Sambuc
165*11be35a1SLionel Sambuc const char** last_variable = user_variables;
166*11be35a1SLionel Sambuc int ch;
167*11be35a1SLionel Sambuc while ((ch = getopt(argc, argv, GETOPT_PLUS":v:")) != -1) {
168*11be35a1SLionel Sambuc switch (ch) {
169*11be35a1SLionel Sambuc case 'v':
170*11be35a1SLionel Sambuc *last_variable++ = optarg;
171*11be35a1SLionel Sambuc break;
172*11be35a1SLionel Sambuc
173*11be35a1SLionel Sambuc case ':':
174*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "%s's -%c requires an argument", argv[0],
175*11be35a1SLionel Sambuc optopt);
176*11be35a1SLionel Sambuc
177*11be35a1SLionel Sambuc case '?':
178*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "Unknown %s option -%c", argv[0], optopt);
179*11be35a1SLionel Sambuc
180*11be35a1SLionel Sambuc default:
181*11be35a1SLionel Sambuc assert(false);
182*11be35a1SLionel Sambuc }
183*11be35a1SLionel Sambuc }
184*11be35a1SLionel Sambuc argc -= optind;
185*11be35a1SLionel Sambuc argv += optind;
186*11be35a1SLionel Sambuc *last_variable = NULL;
187*11be35a1SLionel Sambuc
188*11be35a1SLionel Sambuc if (argc != 3)
189*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "Must provide a test program, a test case name "
190*11be35a1SLionel Sambuc "and a result file");
191*11be35a1SLionel Sambuc const char* test_program = argv[0];
192*11be35a1SLionel Sambuc const char* test_case = argv[1];
193*11be35a1SLionel Sambuc const char* result_file = argv[2];
194*11be35a1SLionel Sambuc
195*11be35a1SLionel Sambuc bool success;
196*11be35a1SLionel Sambuc check_error(tester->run_test_case(test_program, test_case, result_file,
197*11be35a1SLionel Sambuc user_variables, run_params, &success));
198*11be35a1SLionel Sambuc return success ? EXIT_SUCCESS : EXIT_FAILURE;
199*11be35a1SLionel Sambuc }
200*11be35a1SLionel Sambuc
201*11be35a1SLionel Sambuc
202*11be35a1SLionel Sambuc /// Generic entry point to the tester's command-line interface.
203*11be35a1SLionel Sambuc ///
204*11be35a1SLionel Sambuc /// \param argc Verbatim argc passed to main().
205*11be35a1SLionel Sambuc /// \param argv Verbatim argv passed to main().
206*11be35a1SLionel Sambuc /// \param tester Description of the tester implemented by this binary.
207*11be35a1SLionel Sambuc ///
208*11be35a1SLionel Sambuc /// \return An exit status.
209*11be35a1SLionel Sambuc ///
210*11be35a1SLionel Sambuc /// \post Usage errors terminate the execution of the program right away.
211*11be35a1SLionel Sambuc int
kyua_cli_main(int argc,char * const * argv,const kyua_cli_tester_t * tester)212*11be35a1SLionel Sambuc kyua_cli_main(int argc, char* const* argv, const kyua_cli_tester_t* tester)
213*11be35a1SLionel Sambuc {
214*11be35a1SLionel Sambuc kyua_run_params_t run_params;
215*11be35a1SLionel Sambuc kyua_run_params_init(&run_params);
216*11be35a1SLionel Sambuc
217*11be35a1SLionel Sambuc int ch;
218*11be35a1SLionel Sambuc while ((ch = getopt(argc, argv, GETOPT_PLUS":g:t:u:")) != -1) {
219*11be35a1SLionel Sambuc switch (ch) {
220*11be35a1SLionel Sambuc case 'g':
221*11be35a1SLionel Sambuc run_params.unprivileged_group = (uid_t)parse_ulong(
222*11be35a1SLionel Sambuc optarg, "Invalid GID", GID_MAX);
223*11be35a1SLionel Sambuc break;
224*11be35a1SLionel Sambuc
225*11be35a1SLionel Sambuc case 't':
226*11be35a1SLionel Sambuc run_params.timeout_seconds = parse_ulong(
227*11be35a1SLionel Sambuc optarg, "Invalid timeout value", LONG_MAX);
228*11be35a1SLionel Sambuc break;
229*11be35a1SLionel Sambuc
230*11be35a1SLionel Sambuc case 'u':
231*11be35a1SLionel Sambuc run_params.unprivileged_user = (uid_t)parse_ulong(
232*11be35a1SLionel Sambuc optarg, "Invalid UID", UID_MAX);
233*11be35a1SLionel Sambuc break;
234*11be35a1SLionel Sambuc
235*11be35a1SLionel Sambuc case ':':
236*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "-%c requires an argument", optopt);
237*11be35a1SLionel Sambuc
238*11be35a1SLionel Sambuc case '?':
239*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "Unknown option -%c", optopt);
240*11be35a1SLionel Sambuc
241*11be35a1SLionel Sambuc default:
242*11be35a1SLionel Sambuc assert(false);
243*11be35a1SLionel Sambuc }
244*11be35a1SLionel Sambuc }
245*11be35a1SLionel Sambuc argc -= optind;
246*11be35a1SLionel Sambuc argv += optind;
247*11be35a1SLionel Sambuc reset_getopt();
248*11be35a1SLionel Sambuc
249*11be35a1SLionel Sambuc if (argc == 0)
250*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "Must provide a command");
251*11be35a1SLionel Sambuc const char* command = argv[0];
252*11be35a1SLionel Sambuc
253*11be35a1SLionel Sambuc // Keep sorted by order of likelyhood (yeah, micro-optimization).
254*11be35a1SLionel Sambuc if (strcmp(command, "test") == 0)
255*11be35a1SLionel Sambuc return test_command(argc, argv, tester, &run_params);
256*11be35a1SLionel Sambuc else if (strcmp(command, "list") == 0)
257*11be35a1SLionel Sambuc return list_command(argc, argv, tester, &run_params);
258*11be35a1SLionel Sambuc else
259*11be35a1SLionel Sambuc errx(EXIT_USAGE_ERROR, "Unknown command '%s'", command);
260*11be35a1SLionel Sambuc }
261