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#if defined(__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.size())
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 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
144 {
145 }
146
147 Command(Argv argv_arg)
148 : argv(std::move(argv_arg))
149 {
150 }
151
152 Command *copy() const
153 {
154 Command *ret = new Command;
155 ret->argv = argv;
156 ret->mark = mark;
157 return ret;
158 }
159
160 virtual void execute(std::ostream &os) override
161 {
162 if (!argv.empty())
163 {
164 os << to_string() << std::endl;
165#ifdef OPENVPN_PROCESS_AVOID_PIPES
166 const int status = system_cmd(argv[0], argv);
167 if (status < 0)
168 os << "Error: command failed to execute" << std::endl;
169#else
171 const int status = system_cmd(argv[0], argv, nullptr, inout, RedirectPipe::COMBINE_OUT_ERR, nullptr);
172 if (status < 0)
173 os << "Error: command failed to execute" << std::endl;
174 os << inout.out;
175
176 /* if route already exists, we got "File exists" in output */
177 if (inout.out.find("File exists") != std::string::npos)
178 throw Exception("Route already exists, new route will be ignored");
179#endif
180 }
181 else
182 os << "Error: command called with empty argv" << std::endl;
183 }
184
185 virtual std::string to_string() const override
186 {
187 return argv.to_string();
188 }
189
191};
192
193} // namespace openvpn
194
195#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:266
char ** environ
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95
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:152
virtual std::string to_string() const override
Definition process.hpp:185
virtual void execute(std::ostream &os) override
Definition process.hpp:160
RCPtr< Command > Ptr
Definition process.hpp:141
Command(Argv argv_arg)
Definition process.hpp:147
virtual void redirect()=0
virtual void close()=0
std::string ret