• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
3  * Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it would be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write the Free Software Foundation,
17  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
20  */
21 
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include "test.h"
30 #include "tst_cmd.h"
31 
32 #define OPEN_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
33 #define OPEN_FLAGS	(O_WRONLY | O_APPEND | O_CREAT)
34 
tst_cmd_fds_(void (cleanup_fn)(void),const char * const argv[],int stdout_fd,int stderr_fd,enum tst_cmd_flags flags)35 int tst_cmd_fds_(void (cleanup_fn)(void),
36 		const char *const argv[],
37 		int stdout_fd,
38 		int stderr_fd,
39 		enum tst_cmd_flags flags)
40 {
41 	int rc;
42 
43 	if (argv == NULL || argv[0] == NULL) {
44 		tst_brkm(TBROK, cleanup_fn,
45 			"argument list is empty at %s:%d", __FILE__, __LINE__);
46 		return -1;
47 	}
48 
49 	/*
50 	 * The tst_sig() install poisoned signal handlers for all signals the
51 	 * test is not expected to get.
52 	 *
53 	 * So we temporarily disable the handler for sigchild we get after our
54 	 * child exits so that we don't have to disable it in each test that
55 	 * uses this interface.
56 	 */
57 	void *old_handler = signal(SIGCHLD, SIG_DFL);
58 
59 	char path[PATH_MAX];
60 
61 	if (tst_get_path(argv[0], path, sizeof(path))) {
62 		if (flags & TST_CMD_TCONF_ON_MISSING)
63 			tst_brkm(TCONF, cleanup_fn, "Couldn't find '%s' in $PATH at %s:%d", argv[0],
64 				 __FILE__, __LINE__);
65 		else
66 			return 255;
67 	}
68 
69 	pid_t pid = vfork();
70 	if (pid == -1) {
71 		tst_brkm(TBROK | TERRNO, cleanup_fn, "vfork failed at %s:%d",
72 			__FILE__, __LINE__);
73 		return -1;
74 	}
75 	if (!pid) {
76 		/* redirecting stdout and stderr if needed */
77 		if (stdout_fd != -1) {
78 			close(STDOUT_FILENO);
79 			dup2(stdout_fd, STDOUT_FILENO);
80 		}
81 
82 		if (stderr_fd != -1) {
83 			close(STDERR_FILENO);
84 			dup2(stderr_fd, STDERR_FILENO);
85 		}
86 
87 		execvp(argv[0], (char *const *)argv);
88 		_exit(254);
89 	}
90 
91 	int ret = -1;
92 	if (waitpid(pid, &ret, 0) != pid) {
93 		tst_brkm(TBROK | TERRNO, cleanup_fn, "waitpid failed at %s:%d",
94 			__FILE__, __LINE__);
95 		return -1;
96 	}
97 
98 	signal(SIGCHLD, old_handler);
99 
100 	if (!WIFEXITED(ret)) {
101 		tst_brkm(TBROK, cleanup_fn, "failed to exec cmd '%s' at %s:%d",
102 			argv[0], __FILE__, __LINE__);
103 		return -1;
104 	}
105 
106 	rc = WEXITSTATUS(ret);
107 
108 	if (!(flags & TST_CMD_PASS_RETVAL) && rc) {
109 		tst_brkm(TBROK, cleanup_fn,
110 			 "'%s' exited with a non-zero code %d at %s:%d",
111 			 argv[0], rc, __FILE__, __LINE__);
112 		return -1;
113 	}
114 
115 	return rc;
116 }
117 
tst_cmd_(void (cleanup_fn)(void),const char * const argv[],const char * stdout_path,const char * stderr_path,enum tst_cmd_flags flags)118 int tst_cmd_(void (cleanup_fn)(void),
119 		const char *const argv[],
120 		const char *stdout_path,
121 		const char *stderr_path,
122 		enum tst_cmd_flags flags)
123 {
124 	int stdout_fd = -1;
125 	int stderr_fd = -1;
126 	int rc;
127 
128 	if (stdout_path != NULL) {
129 		stdout_fd = open(stdout_path,
130 				OPEN_FLAGS, OPEN_MODE);
131 
132 		if (stdout_fd == -1)
133 			tst_resm(TWARN | TERRNO,
134 				"open() on %s failed at %s:%d",
135 				stdout_path, __FILE__, __LINE__);
136 	}
137 
138 	if (stderr_path != NULL) {
139 		stderr_fd = open(stderr_path,
140 				OPEN_FLAGS, OPEN_MODE);
141 
142 		if (stderr_fd == -1)
143 			tst_resm(TWARN | TERRNO,
144 				"open() on %s failed at %s:%d",
145 				stderr_path, __FILE__, __LINE__);
146 	}
147 
148 	rc = tst_cmd_fds(cleanup_fn, argv, stdout_fd, stderr_fd, flags);
149 
150 	if ((stdout_fd != -1) && (close(stdout_fd) == -1))
151 		tst_resm(TWARN | TERRNO,
152 			"close() on %s failed at %s:%d",
153 			stdout_path, __FILE__, __LINE__);
154 
155 	if ((stderr_fd != -1) && (close(stderr_fd) == -1))
156 		tst_resm(TWARN | TERRNO,
157 			"close() on %s failed at %s:%d",
158 			stderr_path, __FILE__, __LINE__);
159 
160 	return rc;
161 }
162 
tst_system(const char * command)163 int tst_system(const char *command)
164 {
165 	int ret = 0;
166 
167 	/*
168 	 *Temporarily disable SIGCHLD of user defined handler, so the
169 	 *system(3) function will not cause unexpected SIGCHLD signal
170 	 *callback function for test cases.
171 	 */
172 	void *old_handler = signal(SIGCHLD, SIG_DFL);
173 
174 	ret = system(command);
175 
176 	signal(SIGCHLD, old_handler);
177 	return ret;
178 }
179