- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gnewprocess_unix_exec_enabled.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will 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, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gnewprocess_unix_exec_enabled.cpp
19 //
20
21 #include "gdef.h"
22 #include "glimits.h"
23 #include "gnewprocess.h"
24 #include "gprocess.h"
25 #include "gidentity.h"
26 #include "gassert.h"
27 #include "gfs.h"
28 #include "glog.h"
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <sys/stat.h>
33 #include <fcntl.h> // open()
34 #include <unistd.h> // setuid() etc
35 #include <algorithm> // std::swap()
36 #include <utility> // std::swap()
37 #include <iostream>
38
39 namespace
40 {
41 void noCloseOnExec( int fd )
42 {
43 ::fcntl( fd , F_SETFD , 0 ) ;
44 }
45 }
46
47 namespace G
48 {
49 class Pipe ;
50 }
51
55 class G::Pipe
56 {
57 public:
58 explicit Pipe( bool active ) ;
59 ~Pipe() ;
60 void inChild() ; // writer
61 void inParent() ; // reader
62 int fd() const ;
63 void dup() ; // onto stdout
64 std::string read() ; // size-limited
65 void write( const std::string & ) ;
66 private:
67 G_EXCEPTION( Error , "pipe error" ) ;
68 int m_fds[2] ;
69 int m_fd ;
70 } ;
71
75 class G::NewProcess::ChildProcessImp
76 {
77 public:
78 ChildProcessImp() ;
79 unsigned long m_ref_count ;
80 Process::Id m_id ;
81 Pipe m_pipe ;
82 private:
83 void operator=( const ChildProcessImp & ) ;
84 ChildProcessImp( const ChildProcessImp & ) ;
85 } ;
86
87 // ===
88
89 G::NewProcess::ChildProcess::ChildProcess( ChildProcessImp * imp ) :
90 m_imp(imp)
91 {
92 m_imp->m_ref_count = 1 ;
93 }
94
95 G::NewProcess::ChildProcess::~ChildProcess()
96 {
97 m_imp->m_ref_count-- ;
98 if( m_imp->m_ref_count == 0 )
99 delete m_imp ;
100 }
101
102 G::NewProcess::ChildProcess::ChildProcess( const ChildProcess & other ) :
103 m_imp(other.m_imp)
104 {
105 m_imp->m_ref_count++ ;
106 }
107
108 void G::NewProcess::ChildProcess::operator=( const ChildProcess & rhs )
109 {
110 ChildProcess temp( rhs ) ;
111 std::swap( m_imp , temp.m_imp ) ;
112 }
113
114 int G::NewProcess::ChildProcess::wait()
115 {
116 return G::NewProcess::wait( m_imp->m_id , 127 ) ;
117 }
118
119 std::string G::NewProcess::ChildProcess::read()
120 {
121 return m_imp->m_pipe.read() ;
122 }
123
124 // ===
125
126 G::NewProcess::ChildProcessImp::ChildProcessImp() :
127 m_ref_count(0UL) ,
128 m_pipe(true)
129 {
130 }
131
132 // ===
133
134 G::NewProcess::Who G::NewProcess::fork()
135 {
136 Process::Id id ;
137 return fork( id ) ;
138 }
139
140 G::NewProcess::Who G::NewProcess::fork( Process::Id & child_pid )
141 {
142 std::cout << std::flush ;
143 std::cerr << std::flush ;
144 pid_t rc = ::fork() ;
145 const bool ok = rc != -1 ;
146 if( ok )
147 {
148 if( rc != 0 )
149 child_pid.m_pid = rc ;
150 }
151 else
152 {
153 throw CannotFork() ;
154 }
155 return rc == 0 ? Child : Parent ;
156 }
157
158 int G::NewProcess::wait( const Process::Id & child_pid )
159 {
160 int status ;
161 for(;;)
162 {
163 G_DEBUG( "G::NewProcess::wait: waiting" ) ;
164 int rc = ::waitpid( child_pid.m_pid , &status , 0 ) ;
165 if( rc == -1 && Process::errno_() == EINTR )
166 {
167 ; // signal in parent -- keep waiting
168 }
169 else if( rc == -1 )
170 {
171 int error = Process::errno_() ;
172 std::ostringstream ss ;
173 ss << "errno=" << error ;
174 throw WaitError( ss.str() ) ;
175 }
176 else
177 {
178 break ;
179 }
180 }
181 G_DEBUG( "G::NewProcess::wait: done" ) ;
182
183 if( ! WIFEXITED(status) )
184 {
185 // uncaught signal or stopped
186 std::ostringstream ss ;
187 ss << "status=" << status ;
188 throw ChildError( ss.str() ) ;
189 }
190
191 const int exit_status = WEXITSTATUS(status) ;
192 return exit_status ;
193 }
194
195 int G::NewProcess::wait( const Process::Id & child_pid , int error_return )
196 {
197 try
198 {
199 return wait( child_pid ) ;
200 }
201 catch(...)
202 {
203 }
204 return error_return ;
205 }
206
207 G::NewProcess::ChildProcess G::NewProcess::spawn( const Path & exe , const Strings & args )
208 {
209 ChildProcess child( new ChildProcessImp ) ;
210 if( fork(child.m_imp->m_id) == Child )
211 {
212 try
213 {
214 child.m_imp->m_pipe.inChild() ;
215 Process::closeFiles( child.m_imp->m_pipe.fd() ) ;
216 child.m_imp->m_pipe.dup() ;
217 execCore( exe , args ) ;
218 }
219 catch(...)
220 {
221 }
222 ::_exit( 127 ) ;
223 return ChildProcess(0) ; // pacify the compiler
224 }
225 else
226 {
227 child.m_imp->m_pipe.inParent() ;
228 return child ;
229 }
230 }
231
232 int G::NewProcess::spawn( Identity nobody , const Path & exe , const Strings & args ,
233 std::string * pipe_result_p , int error_return , std::string (*fn)(int) )
234 {
235 if( exe.isRelative() )
236 throw InvalidPath( exe.str() ) ;
237
238 if( Identity::effective().isRoot() || nobody.isRoot() )
239 throw Insecure() ;
240
241 Pipe pipe( pipe_result_p != NULL ) ;
242 Process::Id child_pid ;
243 if( fork(child_pid) == Child )
244 {
245 try
246 {
247 Process::beNobody( nobody ) ;
248 G_ASSERT( ::getuid() != 0U && ::geteuid() != 0U ) ;
249 pipe.inChild() ;
250 Process::closeFiles( pipe.fd() ) ;
251 pipe.dup() ; // dup() onto stdout
252 int error = execCore( exe , args ) ;
253 if( fn != 0 )
254 {
255 std::string s = (*fn)(error) ;
256 ssize_t rc = ::write( STDOUT_FILENO , s.c_str() , s.length() ) ;
257 G_IGNORE_VARIABLE(rc) ;
258 }
259 }
260 catch(...)
261 {
262 }
263 ::_exit( error_return ) ;
264 return error_return ; // pacify the compiler
265 }
266 else
267 {
268 pipe.inParent() ;
269 int exit_status = wait( child_pid , error_return ) ;
270 if( pipe_result_p != NULL ) *pipe_result_p = pipe.read() ;
271 return exit_status ;
272 }
273 }
274
275 int G::NewProcess::execCore( const G::Path & exe , const Strings & args )
276 {
277 char * env[3U] ;
278 std::string path( "PATH=/usr/bin:/bin" ) ; // no "."
279 std::string ifs( "IFS= \t\n" ) ;
280 env[0U] = const_cast<char*>( path.c_str() ) ;
281 env[1U] = const_cast<char*>( ifs.c_str() ) ;
282 env[2U] = NULL ;
283
284 char ** argv = new char* [ args.size() + 2U ] ;
285 std::string str_exe = exe.str() ;
286 argv[0U] = const_cast<char*>( str_exe.c_str() ) ;
287 unsigned int argc = 1U ;
288 for( Strings::const_iterator arg_p = args.begin() ; arg_p != args.end() ; ++arg_p , argc++ )
289 argv[argc] = const_cast<char*>(arg_p->c_str()) ;
290 argv[argc] = NULL ;
291
292 ::execve( exe.str().c_str() , argv , env ) ;
293 const int error = Process::errno_() ;
294 delete [] argv ;
295
296 G_DEBUG( "G::NewProcess::exec: execve() returned: errno=" << error << ": " << exe ) ;
297 return error ;
298 }
299
300 // ===
301
302 G::Pipe::Pipe( bool active ) :
303 m_fd(-1)
304 {
305 m_fds[0] = m_fds[1] = -1 ;
306 if( active && ::pipe( m_fds ) < 0 )
307 throw Error() ;
308 G_DEBUG( "G::Pipe::ctor: " << m_fds[0] << " " << m_fds[1] ) ;
309 }
310
311 G::Pipe::~Pipe()
312 {
313 if( m_fds[0] >= 0 ) ::close( m_fds[0] ) ;
314 if( m_fds[1] >= 0 ) ::close( m_fds[1] ) ;
315 }
316
317 void G::Pipe::inChild()
318 {
319 ::close( m_fds[0] ) ;
320 m_fds[0] = -1 ;
321 m_fd = m_fds[1] ; // writer
322 }
323
324 void G::Pipe::inParent()
325 {
326 ::close( m_fds[1] ) ;
327 m_fds[1] = -1 ;
328 m_fd = m_fds[0] ; // reader
329 }
330
331 int G::Pipe::fd() const
332 {
333 return m_fd ;
334 }
335
336 void G::Pipe::dup()
337 {
338 if( m_fd != -1 && m_fd != STDOUT_FILENO )
339 {
340 if( ::dup2(m_fd,STDOUT_FILENO) != STDOUT_FILENO )
341 throw Error() ;
342 ::close( m_fd ) ;
343 m_fd = -1 ;
344 m_fds[1] = -1 ;
345 noCloseOnExec( STDOUT_FILENO ) ;
346 }
347 }
348
349 std::string G::Pipe::read()
350 {
351 char buffer[limits::pipe_buffer] ;
352 ssize_t rc = m_fd == -1 ? 0 : ::read( m_fd , buffer , sizeof(buffer) ) ;
353 if( rc < 0 ) throw Error("read") ;
354 const size_t buffer_size = static_cast<size_t>(rc) ;
355 return std::string(buffer,buffer_size) ;
356 }
357
G::NewProcess::spawn
static int spawn(Identity nobody, const Path &exe, const Strings &args, std::string *pipe_result_p=NULL, int error_return=127, std::string(*error_decode_fn)(int)=0)
Runs a command in an unprivileged child process.
Definition: gnewprocess_unix_exec_enabled.cpp:232
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:135
G::Identity::isRoot
bool isRoot() const
Returns true if the userid is zero.
Definition: gidentity_unix.cpp:122
G::NewProcess::ChildProcess
friend class ChildProcess
Definition: gnewprocess.h:96
G::NewProcess::ChildProcess::operator=
void operator=(const ChildProcess &)
Definition: gnewprocess_unix_exec_enabled.cpp:108
glog.h
G::Pipe::inParent
void inParent()
Definition: gnewprocess_unix_exec_enabled.cpp:324
gidentity.h
G::Pipe::inChild
void inChild()
Definition: gnewprocess_unix_exec_enabled.cpp:317
G::Pipe::write
void write(const std::string &)
G::Strings
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
G::Identity
A very low-level interface to getpwnam() and the get/set/e/uid/gid functions.
Definition: gidentity.h:41
G::Identity::effective
static Identity effective()
Returns the current effective identity.
Definition: gidentity_unix.cpp:86
G::NewProcess::Parent
Definition: gnewprocess.h:53
G::Pipe::read
std::string read()
Definition: gnewprocess_unix_exec_enabled.cpp:349
G::NewProcess::ChildProcessImp::ChildProcessImp
ChildProcessImp()
Definition: gnewprocess_unix_exec_enabled.cpp:126
G::NewProcess::ChildProcessImp::m_pipe
Pipe m_pipe
Definition: gnewprocess_unix_exec_enabled.cpp:81
G::Path::isRelative
bool isRelative() const
Returns true if the path is a relative path.
Definition: gpath.cpp:145
G::Process::errno_
static int errno_()
Returns the process's current 'errno' value.
Definition: gprocess_unix.cpp:107
gfs.h
glimits.h
G::NewProcess::ChildProcess::~ChildProcess
~ChildProcess()
Definition: gnewprocess_unix_exec_enabled.cpp:95
G_ASSERT
#define G_ASSERT(test)
Definition: gassert.h:30
gassert.h
G
Low-level classes.
G::NewProcess::ChildProcessImp::m_ref_count
unsigned long m_ref_count
Definition: gnewprocess_unix_exec_enabled.cpp:79
G::NewProcess::ChildProcess::wait
int wait()
Definition: gnewprocess_unix_exec_enabled.cpp:114
G::NewProcess::ChildProcess
Represents the state of a child process.
Definition: gnewprocess.h:56
G::NewProcess::fork
static Who fork()
Forks a child process.
Definition: gnewprocess_unix_exec_enabled.cpp:134
gprocess.h
G::Pipe::Pipe
Pipe(bool active)
Definition: gnewprocess_unix_exec_enabled.cpp:302
G_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
G::NewProcess::ChildProcess::read
std::string read()
Definition: gnewprocess_unix_exec_enabled.cpp:119
gnewprocess.h
G::NewProcess::ChildProcessImp
A private implementation class used by G::NewProcess.
Definition: gnewprocess_unix_exec_enabled.cpp:75
G::Pipe::~Pipe
~Pipe()
Definition: gnewprocess_unix_exec_enabled.cpp:311
G_EXCEPTION
#define G_EXCEPTION(class_name, description)
define as a function rather than a type if optimising for size
Definition: gexception.h:93
G::Process::Id
Process-id class.
Definition: gprocess.h:52
G::Pipe::fd
int fd() const
Definition: gnewprocess_unix_exec_enabled.cpp:331
G::NewProcess::Child
Definition: gnewprocess.h:53
G::limits::pipe_buffer
Definition: glimits.h:45
G::Process::closeFiles
static void closeFiles(bool keep_stderr=false)
Closes all open file descriptors.
Definition: gprocess_unix.cpp:70
G::Process::beNobody
static void beNobody(Identity)
If currently running with a real identity of root then the real identity is set to the nobody identit...
Definition: gprocess_unix.cpp:192
G::Pipe::dup
void dup()
Definition: gnewprocess_unix_exec_enabled.cpp:336
G::Path
A Path object represents a file system path.
Definition: gpath.h:44
G::Pipe
A private implementation class used by G::NewProcess.
Definition: gnewprocess_unix_exec_enabled.cpp:55
G::NewProcess::ChildProcessImp::m_id
Process::Id m_id
Definition: gnewprocess_unix_exec_enabled.cpp:80
G::NewProcess::Who
Who
Definition: gnewprocess.h:53