Home | History | Annotate | Download | only in lib
      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 (at) 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 
     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 		return -1;
     46 	}
     47 
     48 	/*
     49 	 * The tst_sig() install poisoned signal handlers for all signals the
     50 	 * test is not expected to get.
     51 	 *
     52 	 * So we temporarily disable the handler for sigchild we get after our
     53 	 * child exits so that we don't have to disable it in each test that
     54 	 * uses this interface.
     55 	 */
     56 	void *old_handler = signal(SIGCHLD, SIG_DFL);
     57 
     58 	pid_t pid = vfork();
     59 	if (pid == -1) {
     60 		tst_brkm(TBROK | TERRNO, cleanup_fn, "vfork failed at %s:%d",
     61 			__FILE__, __LINE__);
     62 		return -1;
     63 	}
     64 	if (!pid) {
     65 		/* redirecting stdout and stderr if needed */
     66 		if (stdout_fd != -1) {
     67 			close(STDOUT_FILENO);
     68 			dup2(stdout_fd, STDOUT_FILENO);
     69 		}
     70 
     71 		if (stderr_fd != -1) {
     72 			close(STDERR_FILENO);
     73 			dup2(stderr_fd, STDERR_FILENO);
     74 		}
     75 
     76 		if (execvp(argv[0], (char *const *)argv)) {
     77 			if (errno == ENOENT)
     78 				_exit(255);
     79 		}
     80 		_exit(254);
     81 	}
     82 
     83 	int ret = -1;
     84 	if (waitpid(pid, &ret, 0) != pid) {
     85 		tst_brkm(TBROK | TERRNO, cleanup_fn, "waitpid failed at %s:%d",
     86 			__FILE__, __LINE__);
     87 		return -1;
     88 	}
     89 
     90 	signal(SIGCHLD, old_handler);
     91 
     92 	if (!WIFEXITED(ret)) {
     93 		tst_brkm(TBROK, cleanup_fn, "failed to exec cmd '%s' at %s:%d",
     94 			argv[0], __FILE__, __LINE__);
     95 		return -1;
     96 	}
     97 
     98 	rc = WEXITSTATUS(ret);
     99 
    100 	if ((!pass_exit_val) && rc) {
    101 		tst_brkm(TBROK, cleanup_fn,
    102 			 "'%s' exited with a non-zero code %d at %s:%d",
    103 			 argv[0], rc, __FILE__, __LINE__);
    104 		return -1;
    105 	}
    106 
    107 	return rc;
    108 }
    109 
    110 int tst_run_cmd_(void (cleanup_fn)(void),
    111 		const char *const argv[],
    112 		const char *stdout_path,
    113 		const char *stderr_path,
    114 		int pass_exit_val)
    115 {
    116 	int stdout_fd = -1;
    117 	int stderr_fd = -1;
    118 	int rc;
    119 
    120 	if (stdout_path != NULL) {
    121 		stdout_fd = open(stdout_path,
    122 				OPEN_FLAGS, OPEN_MODE);
    123 
    124 		if (stdout_fd == -1)
    125 			tst_resm(TWARN | TERRNO,
    126 				"open() on %s failed at %s:%d",
    127 				stdout_path, __FILE__, __LINE__);
    128 	}
    129 
    130 	if (stderr_path != NULL) {
    131 		stderr_fd = open(stderr_path,
    132 				OPEN_FLAGS, OPEN_MODE);
    133 
    134 		if (stderr_fd == -1)
    135 			tst_resm(TWARN | TERRNO,
    136 				"open() on %s failed at %s:%d",
    137 				stderr_path, __FILE__, __LINE__);
    138 	}
    139 
    140 	rc = tst_run_cmd_fds(cleanup_fn, argv, stdout_fd, stderr_fd,
    141 			     pass_exit_val);
    142 
    143 	if ((stdout_fd != -1) && (close(stdout_fd) == -1))
    144 		tst_resm(TWARN | TERRNO,
    145 			"close() on %s failed at %s:%d",
    146 			stdout_path, __FILE__, __LINE__);
    147 
    148 	if ((stderr_fd != -1) && (close(stderr_fd) == -1))
    149 		tst_resm(TWARN | TERRNO,
    150 			"close() on %s failed at %s:%d",
    151 			stderr_path, __FILE__, __LINE__);
    152 
    153 	return rc;
    154 }
    155 
    156 int tst_system(const char *command)
    157 {
    158 	int ret = 0;
    159 
    160 	/*
    161 	 *Temporarily disable SIGCHLD of user defined handler, so the
    162 	 *system(3) function will not cause unexpected SIGCHLD signal
    163 	 *callback function for test cases.
    164 	 */
    165 	void *old_handler = signal(SIGCHLD, SIG_DFL);
    166 
    167 	ret = system(command);
    168 
    169 	signal(SIGCHLD, old_handler);
    170 	return ret;
    171 }
    172