gprocess_unix.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 // gprocess_unix.cpp
19 //
20 
21 #include "gdef.h"
22 #include "glimits.h"
23 #include "gprocess.h"
24 #include "gidentity.h"
25 #include "gassert.h"
26 #include "gfs.h"
27 #include "glog.h"
28 #include <iostream>
29 #include <cstring> // std::strerror()
30 #include <fcntl.h>
31 #include <errno.h>
32 
33 namespace
34 {
35  void noCloseOnExec( int fd )
36  {
37  ::fcntl( fd , F_SETFD , 0 ) ;
38  }
39 }
40 
45 {
46 public:
47  pid_t m_pid ;
48 } ;
49 
50 // ===
51 
52 void G::Process::cd( const Path & dir )
53 {
54  if( ! cd(dir,NoThrow()) )
55  throw CannotChangeDirectory( dir.str() ) ;
56 }
57 
58 bool G::Process::cd( const Path & dir , NoThrow )
59 {
60  return 0 == ::chdir( dir.str().c_str() ) ;
61 }
62 
64 {
65  ::close( STDERR_FILENO ) ;
66  ::open( G::FileSystem::nullDevice() , O_WRONLY ) ;
67  noCloseOnExec( STDERR_FILENO ) ;
68 }
69 
70 void G::Process::closeFiles( bool keep_stderr )
71 {
72  closeFiles( keep_stderr ? STDERR_FILENO : -1 ) ;
73 }
74 
75 void G::Process::closeFiles( int keep )
76 {
77  G_ASSERT( keep == -1 || keep >= STDERR_FILENO ) ;
78  std::cout << std::flush ;
79  std::cerr << std::flush ;
80 
81  int n = 256U ;
82  long rc = ::sysconf( _SC_OPEN_MAX ) ;
83  if( rc > 0L )
84  n = static_cast<int>( rc ) ;
85 
86  for( int fd = 0 ; fd < n ; fd++ )
87  {
88  if( fd != keep )
89  ::close( fd ) ;
90  }
91 
92  // reopen standard fds to prevent accidental use
93  // of arbitrary files or sockets as standard
94  // streams
95  //
96  ::open( G::FileSystem::nullDevice() , O_RDONLY ) ;
97  ::open( G::FileSystem::nullDevice() , O_WRONLY ) ;
98  if( keep != STDERR_FILENO )
99  {
100  ::open( G::FileSystem::nullDevice() , O_WRONLY ) ;
101  }
102  noCloseOnExec( STDIN_FILENO ) ;
103  noCloseOnExec( STDOUT_FILENO ) ;
104  noCloseOnExec( STDERR_FILENO ) ;
105 }
106 
108 {
109  return errno ; // not ::errno or std::errno for gcc2.95
110 }
111 
112 int G::Process::errno_( int e )
113 {
114  int old = errno ;
115  errno = e ;
116  return old ;
117 }
118 
119 std::string G::Process::strerror( int errno_ )
120 {
121  char * p = std::strerror( errno_ ) ;
122  return std::string( p ? p : "" ) ;
123 }
124 
126 {
127  if( Identity::real().isRoot() || Identity::effective() != Identity::real() )
128  {
129  gid_t dummy ;
130  G_IGNORE_RETURN(int) ::setgroups( 0U , &dummy ) ; // (only works for root, so ignore the return code)
131  }
132 }
133 
134 G::Identity G::Process::beSpecial( Identity special_identity , bool change_group )
135 {
136  change_group = Identity::real().isRoot() ? change_group : true ;
137  Identity old_identity( Identity::effective() ) ;
138  setEffectiveUserTo( special_identity ) ;
139  if( change_group )
140  setEffectiveGroupTo( special_identity ) ;
141  return old_identity ;
142 }
143 
144 G::Identity G::Process::beSpecial( SignalSafe safe , Identity special_identity , bool change_group )
145 {
146  change_group = Identity::real().isRoot() ? change_group : true ;
147  Identity old_identity( Identity::effective() ) ;
148  setEffectiveUserTo( safe , special_identity ) ;
149  if( change_group )
150  setEffectiveGroupTo( safe , special_identity ) ;
151  return old_identity ;
152 }
153 
154 G::Identity G::Process::beOrdinary( Identity nobody , bool change_group )
155 {
156  Identity special_identity( Identity::effective() ) ;
157  if( Identity::real().isRoot() )
158  {
159  setEffectiveUserTo( Identity::root() ) ;
160  if( change_group )
161  setEffectiveGroupTo( nobody ) ;
162  setEffectiveUserTo( nobody ) ;
163  }
164  else
165  {
166  setEffectiveUserTo( Identity::real() ) ;
167  if( change_group )
168  setEffectiveGroupTo( Identity::real() ) ;
169  }
170  return special_identity ;
171 }
172 
173 G::Identity G::Process::beOrdinary( SignalSafe safe , Identity nobody , bool change_group )
174 {
175  Identity special_identity( Identity::effective() ) ;
176  if( Identity::real().isRoot() )
177  {
178  setEffectiveUserTo( safe , Identity::root() ) ;
179  if( change_group )
180  setEffectiveGroupTo( safe , nobody ) ;
181  setEffectiveUserTo( safe , nobody ) ;
182  }
183  else
184  {
185  setEffectiveUserTo( safe , Identity::real() ) ;
186  if( change_group )
187  setEffectiveGroupTo( safe , Identity::real() ) ;
188  }
189  return special_identity ;
190 }
191 
193 {
194  if( Identity::real().isRoot() )
195  {
196  setEffectiveUserTo( Identity::root() ) ;
197  setRealGroupTo( nobody ) ;
198  setRealUserTo( nobody ) ;
199  }
200 }
201 
202 // ===
203 
205 {
206  m_pid = ::getpid() ;
207 }
208 
209 G::Process::Id::Id( SignalSafe , const char * path ) :
210  m_pid(0)
211 {
212  // signal-safe, reentrant implementation suitable for a signal handler...
213  int fd = ::open( path ? path : "" , O_RDONLY ) ;
214  if( fd >= 0 )
215  {
216  const size_t buffer_size = 11U ;
217  char buffer[buffer_size] ;
218  buffer[0U] = '\0' ;
219  ssize_t rc = ::read( fd , buffer , buffer_size - 1U ) ;
220  ::close( fd ) ;
221  for( const char * p = buffer ; rc > 0 && *p >= '0' && *p <= '9' ; p++ , rc-- )
222  {
223  m_pid *= 10 ;
224  m_pid += ( *p - '0' ) ;
225  }
226  }
227 }
228 
229 G::Process::Id::Id( std::istream & stream )
230 {
231  stream >> m_pid ;
232  if( !stream.good() )
233  throw Process::InvalidId() ;
234 }
235 
236 std::string G::Process::Id::str() const
237 {
238  std::ostringstream ss ;
239  ss << m_pid ;
240  return ss.str() ;
241 }
242 
243 bool G::Process::Id::operator==( const Id & other ) const
244 {
245  return m_pid == other.m_pid ;
246 }
247 
248 // ===
249 
250 namespace
251 {
252  mode_t umask_value( G::Process::Umask::Mode mode )
253  {
254  mode_t m = 0 ;
255  if( mode == G::Process::Umask::Tightest ) m = 0177 ; // -rw-------
256  if( mode == G::Process::Umask::Tighter ) m = 0117 ; // -rw-rw----
257  if( mode == G::Process::Umask::Readable ) m = 0133 ; // -rw-r--r--
258  if( mode == G::Process::Umask::GroupOpen ) m = 0113 ;// -rw-rw-r--
259  return m ;
260  }
261 }
262 
265 {
266 public:
267  mode_t m_old_mode ;
268 } ;
269 
271  m_imp(new UmaskImp)
272 {
273  m_imp->m_old_mode = ::umask( umask_value(mode) ) ;
274 }
275 
277 {
278  G_IGNORE_RETURN(mode_t) ::umask( m_imp->m_old_mode ) ;
279  delete m_imp ;
280 }
281 
283 {
284  G_IGNORE_RETURN(mode_t) ::umask( umask_value(mode) ) ;
285 }
286 
std::string str() const
Returns the path string.
Definition: gpath.cpp:135
bool isRoot() const
Returns true if the userid is zero.
A private implementation class used by G::Process::Umask.
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:35
static Identity root()
Returns the superuser identity.
static Identity real()
Returns the calling process's real identity.
static const char * nullDevice()
Definition: gfs_unix.cpp:24
std::string str() const
static void closeStderr()
Closes stderr.
A very low-level interface to getpwnam() and the get/set/e/uid/gid functions.
Definition: gidentity.h:41
static Identity effective()
Returns the current effective identity.
static int errno_()
Returns the process's current 'errno' value.
#define G_ASSERT(test)
Definition: gassert.h:30
static Identity beOrdinary(Identity nobody, bool change_group=true)
Revokes special privileges (root or suid).
static Identity beSpecial(Identity special, bool change_group=true)
Re-acquires special privileges (either root or suid).
bool operator==(const Id &) const
An overload discriminator for Process.
Definition: gprocess.h:76
static void revokeExtraGroups()
Revokes secondary group memberships if really root or if suid.
Process-id class.
Definition: gprocess.h:52
static void cd(const Path &dir)
Changes directory.
static void closeFiles(bool keep_stderr=false)
Closes all open file descriptors.
static void beNobody(Identity)
If currently running with a real identity of root then the real identity is set to the nobody identit...
A private implementation class used by G::Process.
A Path object represents a file system path.
Definition: gpath.h:44
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
static void set(Mode)