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