]> git.proxmox.com Git - ceph.git/blob - ceph/src/pmdk/src/librpmem/rpmem_cmd.c
import ceph 16.2.7
[ceph.git] / ceph / src / pmdk / src / librpmem / rpmem_cmd.c
1 // SPDX-License-Identifier: BSD-3-Clause
2 /* Copyright 2016-2020, Intel Corporation */
3
4 /*
5 * rpmem_cmd.c -- simple interface for running an executable in child process
6 */
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <stdint.h>
12 #include <fcntl.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/wait.h>
16 #include <signal.h>
17 #include <errno.h>
18
19 #include "util.h"
20 #include "out.h"
21 #include "os.h"
22 #include "rpmem_common.h"
23 #include "rpmem_util.h"
24 #include "rpmem_cmd.h"
25
26 /*
27 * rpmem_cmd_init -- initialize command
28 */
29 struct rpmem_cmd *
30 rpmem_cmd_init(void)
31 {
32 struct rpmem_cmd *cmd = calloc(1, sizeof(*cmd));
33 if (!cmd) {
34 RPMEM_LOG(ERR, "allocating command buffer");
35 goto err_alloc_cmd;
36 }
37
38 return cmd;
39 err_alloc_cmd:
40 return NULL;
41 }
42
43 /*
44 * rpmem_cmd_fini -- deinitialize command
45 */
46 void
47 rpmem_cmd_fini(struct rpmem_cmd *cmd)
48 {
49 for (int i = 0; i < cmd->args.argc; i++)
50 free(cmd->args.argv[i]);
51 free(cmd->args.argv);
52 free(cmd);
53 }
54
55 /*
56 * rpmem_cmd_push -- push back command's argument
57 */
58 int
59 rpmem_cmd_push(struct rpmem_cmd *cmd, const char *arg)
60 {
61 size_t argv_count = (size_t)cmd->args.argc + 2;
62 char **argv = realloc(cmd->args.argv, argv_count * sizeof(char *));
63 if (!argv) {
64 RPMEM_LOG(ERR, "reallocating command argv");
65 goto err_realloc;
66 }
67
68 cmd->args.argv = argv;
69
70 char *arg_dup = strdup(arg);
71 if (!arg_dup) {
72 RPMEM_LOG(ERR, "allocating argument");
73 goto err_strdup;
74 }
75
76 cmd->args.argv[cmd->args.argc] = arg_dup;
77 cmd->args.argc++;
78 cmd->args.argv[cmd->args.argc] = NULL;
79
80 return 0;
81 err_strdup:
82 err_realloc:
83 return -1;
84 }
85
86 /*
87 * rpmem_cmd_log -- print executing command
88 */
89 static void
90 rpmem_cmd_log(struct rpmem_cmd *cmd)
91 {
92 RPMEM_ASSERT(cmd->args.argc > 0);
93
94 size_t size = 0;
95 for (int i = 0; i < cmd->args.argc; i++) {
96 size += strlen(cmd->args.argv[i]) + 1;
97 }
98
99 char *buff = malloc(size);
100 if (!buff) {
101 RPMEM_LOG(ERR, "allocating log buffer for command");
102 return;
103 }
104
105 size_t pos = 0;
106
107 for (int i = 0; pos < size && i < cmd->args.argc; i++) {
108 int ret = util_snprintf(&buff[pos], size - pos, "%s%s",
109 cmd->args.argv[i], i == cmd->args.argc - 1 ?
110 "" : " ");
111 if (ret < 0) {
112 RPMEM_LOG(ERR, "!snprintf");
113 goto out;
114 }
115
116 pos += (size_t)ret;
117 }
118
119 RPMEM_LOG(INFO, "executing command '%s'", buff);
120
121 out:
122 free(buff);
123 }
124
125 /*
126 * rpmem_cmd_run -- run command and connect with stdin, stdout and stderr
127 * using unix sockets.
128 *
129 * The communication with child process is done via socketpairs on
130 * stdin, stdout and stderr. The socketpairs are used instead of pipes
131 * because reading from disconnected pipe causes a SIGPIPE signal.
132 * When using socketpair it is possible to read data using recv(3)
133 * function with MSG_NOSIGNAL flag, which doesn't send a signal.
134 */
135 int
136 rpmem_cmd_run(struct rpmem_cmd *cmd)
137 {
138 int fd_in[2];
139 int fd_out[2];
140 int fd_err[2];
141
142 rpmem_cmd_log(cmd);
143
144 /* socketpair for stdin */
145 int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd_in);
146 if (ret < 0) {
147 RPMEM_LOG(ERR, "creating pipe for stdin");
148 goto err_pipe_in;
149 }
150
151 /* parent process stdin socket */
152 cmd->fd_in = fd_in[1];
153
154 /* socketpair for stdout */
155 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd_out);
156 if (ret < 0) {
157 RPMEM_LOG(ERR, "creating pipe for stdout");
158 goto err_pipe_out;
159 }
160
161 /* parent process stdout socket */
162 cmd->fd_out = fd_out[0];
163
164 /* socketpair for stderr */
165 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd_err);
166 if (ret < 0) {
167 RPMEM_LOG(ERR, "creating pipe for stderr");
168 goto err_pipe_err;
169 }
170
171 /* socketpair for stderr */
172 cmd->fd_err = fd_err[0];
173
174 cmd->pid = fork();
175
176 if (cmd->pid == -1) {
177 RPMEM_LOG(ERR, "forking command");
178 goto err_fork;
179 }
180
181 if (!cmd->pid) {
182 dup2(fd_in[0], 0);
183 dup2(fd_out[1], 1);
184 dup2(fd_err[1], 2);
185
186 execvp(cmd->args.argv[0], cmd->args.argv);
187 exit(EXIT_FAILURE);
188 }
189
190 os_close(fd_in[0]);
191 os_close(fd_out[1]);
192 os_close(fd_err[1]);
193
194 return 0;
195 err_fork:
196 os_close(fd_err[0]);
197 os_close(fd_err[1]);
198 err_pipe_err:
199 os_close(fd_out[0]);
200 os_close(fd_out[1]);
201 err_pipe_out:
202 os_close(fd_in[0]);
203 os_close(fd_in[1]);
204 err_pipe_in:
205 return -1;
206 }
207
208 /*
209 * rpmem_cmd_wait -- wait for process to change state
210 */
211 int
212 rpmem_cmd_wait(struct rpmem_cmd *cmd, int *status)
213 {
214 if (cmd->pid <= 0) {
215 RPMEM_LOG(ERR, "wrong PID: %i", cmd->pid);
216 errno = EINVAL;
217 return -1;
218 }
219
220 if (waitpid(cmd->pid, status, 0) != cmd->pid) {
221 RPMEM_LOG(ERR, "!waitpid failed");
222 return -1;
223 }
224
225 return 0;
226 }
227
228 /*
229 * rpmem_cmd_term -- close child process's unix sockets
230 */
231 void
232 rpmem_cmd_term(struct rpmem_cmd *cmd)
233 {
234 os_close(cmd->fd_in);
235 os_close(cmd->fd_out);
236 os_close(cmd->fd_err);
237
238 RPMEM_ASSERT(cmd->pid > 0);
239 }