OpenVPN 3 Core Library
Loading...
Searching...
No Matches
process.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// General-purpose classes for instantiating a posix process with arguments.
13
14#pragma once
15#ifndef _WIN32
16
17#include <stdlib.h> // exit
18#include <unistd.h> // fork, execve
19#include <sys/types.h> // waitpid
20#include <sys/wait.h> // waitpid
21
22#include <string>
23#include <memory>
24#include <utility>
25
31
32namespace openvpn {
33
34// low-level fork/exec (async)
35inline pid_t system_cmd_async(const std::string &cmd,
36 const Argv &argv,
37 const Environ *env,
38 RedirectBase *redir,
39 const sigset_t *sigmask)
40{
41 ArgvWrapper argv_wrap(argv);
42 std::unique_ptr<ArgvWrapper> env_wrap;
43 if (env)
44 env_wrap.reset(new ArgvWrapper(*env));
45 auto fn = cmd.c_str();
46 auto av = argv_wrap.c_argv();
47 auto ev = env_wrap ? env_wrap->c_argv() : ::environ;
48
49#ifdef __APPLE__
50 /* macOS vfork is deprecated and behaves identical to fork() */
51 const pid_t pid = ::fork();
52#else
53 const pid_t pid = (redir || sigmask) ? ::fork() : ::vfork();
54#endif
55
56 if (pid == pid_t(0)) /* child side */
57 {
58 // Only Async-signal-safe functions as specified by
59 // SIGNAL(7) can be called between here and _exit().
60 if (sigmask)
61 ::pthread_sigmask(SIG_SETMASK, sigmask, 0);
62 if (redir)
63 redir->redirect();
64 ::execve(fn, av, ev);
65 ::_exit(127);
66 }
67 else if (pid < pid_t(0)) /* fork failed */
68 return -1;
69 else /* parent side */
70 {
71 if (redir)
72 redir->close();
73 return pid;
74 }
75}
76
77// completion for system_cmd_async()
78inline int system_cmd_post(const pid_t pid)
79{
80 int status = -1;
81 if (::waitpid(pid, &status, 0) == pid)
82 {
83 if (WIFEXITED(status))
84 return WEXITSTATUS(status);
85 }
86 return -1;
87}
88
89// synchronous version of system_cmd_async
90inline int system_cmd(const std::string &cmd,
91 const Argv &argv,
92 RedirectBase *redir,
93 const Environ *env,
94 const sigset_t *sigmask)
95{
96 const pid_t pid = system_cmd_async(cmd, argv, env, redir, sigmask);
97 if (pid < pid_t(0))
98 return -1;
99 return system_cmd_post(pid);
100}
101
102// simple command execution
103inline int system_cmd(const std::string &cmd, const Argv &argv)
104{
105 return system_cmd(cmd, argv, nullptr, nullptr, nullptr);
106}
107
108// simple command execution
109inline int system_cmd(const Argv &argv)
110{
111 int ret = -1;
112 if (!argv.empty())
113 ret = system_cmd(argv[0], argv);
114 return ret;
115}
116
117// command execution with std::strings as
118// input/output/error (uses pipes under the
119// hood)
120inline int system_cmd(const std::string &cmd,
121 const Argv &argv,
122 const Environ *env,
123 RedirectPipe::InOut &inout,
124 unsigned int redirect_pipe_flags,
125 const sigset_t *sigmask)
126{
127 const SignalBlockerPipe sbpipe;
128 RedirectPipe remote;
129 if (!inout.in.empty())
130 redirect_pipe_flags |= RedirectPipe::ENABLE_IN;
131 RedirectPipe local(remote, redirect_pipe_flags);
132 const pid_t pid = system_cmd_async(cmd, argv, env, &remote, sigmask);
133 if (pid < pid_t(0))
134 return -1;
135 local.transact(inout);
136 return system_cmd_post(pid);
137}
138
139struct Command : public Action
140{
142
143 Command() = default;
144
145 Command(Argv argv_arg)
146 : argv(std::move(argv_arg))
147 {
148 }
149
150 Command *copy() const
151 {
152 Command *ret = new Command;
153 ret->argv = argv;
154 ret->mark = mark;
155 return ret;
156 }
157
158 void execute(std::ostream &os) override
159 {
160 if (!argv.empty())
161 {
162 os << to_string() << '\n';
163#ifdef OPENVPN_PROCESS_AVOID_PIPES
164 const int status = system_cmd(argv[0], argv);
165 if (status < 0)
166 os << "Error: command failed to execute\n";
167#else
169 const int status = system_cmd(argv[0], argv, nullptr, inout, RedirectPipe::COMBINE_OUT_ERR, nullptr);
170 if (status < 0)
171 os << "Error: command failed to execute\n";
172 os << inout.out;
173
174 /* if route already exists, we got "File exists" in output */
175 if (inout.out.find("File exists") != std::string::npos)
176 throw Exception("Route already exists, new route will be ignored");
177#endif
178 }
179 else
180 os << "Error: command called with empty argv\n";
181 }
182
183 std::string to_string() const override
184 {
185 return argv.to_string();
186 }
187
189};
190
191} // namespace openvpn
192
193#endif
char *const * c_argv() const noexcept
Definition argv.hpp:81
std::string to_string() const
Definition argv.hpp:30
The smart pointer class.
Definition rc.hpp:119
void transact(InOut &inout)
Definition redir.hpp:262
char ** environ
int system_cmd_post(const pid_t pid)
Definition process.hpp:78
pid_t system_cmd_async(const std::string &cmd, const Argv &argv, const Environ *env, RedirectBase *redir, const sigset_t *sigmask)
Definition process.hpp:35
int system_cmd(const std::string &cmd, const Argv &argv, RedirectBase *redir, const Environ *env, const sigset_t *sigmask)
Definition process.hpp:90
std::string mark
Definition action.hpp:44
Command * copy() const
Definition process.hpp:150
Command()=default
void execute(std::ostream &os) override
Definition process.hpp:158
std::string to_string() const override
Definition process.hpp:183
Command(Argv argv_arg)
Definition process.hpp:145
virtual void redirect()=0
virtual void close()=0
std::string ret
std::ostringstream os