Whamcloud - gitweb
LU-10508 utils: use callvpe() in lustre_rsync
[fs/lustre-release.git] / lustre / utils / callvpe.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of the
9  * License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * LGPL HEADER END
20  *
21  * Copyright (c) 2018, Intel Corporation.
22  */
23
24 #include <signal.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include "callvpe.h"
29
30 /**
31  * callvpe() - execute a file with given arguments and environment.
32  * \param file[in] name or path of file to be executed.
33  * \param args[in] arguments to file.
34  * \param envp[in] execution environment.
35  * \return -1 on failure (for example if fork() failed).
36  * \return process return status on success.
37  *
38  * callvpe() is intended as a safer replacement for system(). It
39  * executes the file specified and returns after it has completed. As
40  * with system during execution of the command, SIGCHLD will be
41  * blocked, and SIGINT and SIGQUIT will be ignored. The main
42  * difference between system(cmd) and callvpe(file, args, envp) is
43  * that system() calls exec("/bin/sh" "-c" "cmd") whereas callvpe()
44  * bypasses the shell and passes the args given directly to execvpe().
45  *
46  * Rather than:
47  *
48  *      snprintf(cmd, sizeof(cmd), "rm -rf %s\n", path);
49  *      rc = system(cmd);
50  *
51  * instead use:
52  *
53  *      char *args[] = { "rm", "-rf", "--", path, NULL };
54  *      extern char **environ;
55  *      rc = callvpe("/bin/rm", args, environ);
56  *
57  * Note that since callvpe() does not use the shell, IO redirection
58  * and pipelines (cmd > /dev/null, cmd 2>&1, cmd1 | cmd2, ...) are not
59  * supported.
60  */
61 int callvpe(const char *file, char *const args[], char *const envp[])
62 {
63         struct sigaction sa = {
64                 .sa_handler = SIG_IGN,
65         };
66         struct sigaction sa_int_saved;
67         struct sigaction sa_quit_saved;
68         sigset_t sigset_saved;
69         pid_t pid;
70         pid_t pid2;
71         int status;
72         int rc;
73
74         sigemptyset(&sa.sa_mask);
75
76         rc = sigaction(SIGINT, &sa, &sa_int_saved);
77         if (rc < 0)
78                 return rc;
79
80         rc = sigaction(SIGQUIT, &sa, &sa_quit_saved);
81         if (rc < 0)
82                 goto out_sa_int;
83
84         sigaddset(&sa.sa_mask, SIGCHLD);
85         rc = sigprocmask(SIG_BLOCK, &sa.sa_mask, &sigset_saved);
86         if (rc < 0)
87                 goto out_sa_quit;
88
89         pid = fork();
90         if (pid < 0) {
91                 rc = -1;
92                 goto out_sigset;
93         }
94
95         if (pid == 0) {
96                 execvpe(file, args, envp);
97                 _exit(127);
98         }
99
100         pid2 = waitpid(pid, &status, 0);
101         if (pid2 < 0) {
102                 rc = -1;
103                 goto out_sigset;
104         }
105
106         rc = status;
107
108 out_sigset:
109         sigprocmask(SIG_SETMASK, &sigset_saved, NULL);
110 out_sa_quit:
111         sigaction(SIGQUIT, &sa_quit_saved, NULL);
112 out_sa_int:
113         sigaction(SIGINT, &sa_int_saved, NULL);
114
115         return rc;
116 }