41 G_EXCEPTION_CLASS( DevNullError ,
tx(
"cannot open /dev/null") ) ;
42 G_EXCEPTION_CLASS( IdentityError ,
tx(
"cannot change process identity") ) ;
43 void noCloseOnExec(
int fd ) noexcept ;
44 void reopen(
int fd ,
int mode ) ;
45 mode_t umaskValue( G::Process::Umask::Mode mode ) ;
46 bool readlink_( string_view path , std::string & value ) ;
47 bool setRealUser( Identity
id , std::nothrow_t ) noexcept ;
48 bool setRealGroup( Identity
id , std::nothrow_t ) noexcept ;
49 void setEffectiveUser( Identity
id ) ;
50 bool setEffectiveUser( Identity
id , std::nothrow_t ) noexcept ;
51 void setEffectiveGroup( Identity
id ) ;
52 bool setEffectiveGroup( Identity
id , std::nothrow_t ) noexcept ;
54 void beSpecial( Identity special_identity ,
bool change_group ) ;
55 void beSpecialForExit( SignalSafe , Identity special_identity ) noexcept ;
56 Identity beOrdinaryAtStartup( Identity ,
bool change_group ) ;
57 Identity beOrdinary( Identity ,
bool change_group ) ;
58 void beOrdinaryForExec( Identity run_as_id ) noexcept ;
59 void revokeExtraGroups() ;
63class G::Process::UmaskImp
67 static mode_t set( Process::Umask::Mode ) noexcept ;
68 static void set( mode_t ) noexcept ;
75 if( !
cd(dir,std::nothrow) )
76 throw CannotChangeDirectory( dir.
str() ) ;
81 return 0 == ::chdir( dir.
cstr() ) ;
86 ProcessImp::reopen( STDERR_FILENO , O_WRONLY ) ;
91 std::cout << std::flush ;
92 std::cerr << std::flush ;
94 ProcessImp::reopen( STDIN_FILENO , O_RDONLY ) ;
95 ProcessImp::reopen( STDOUT_FILENO , O_WRONLY ) ;
97 ProcessImp::reopen( STDERR_FILENO , O_WRONLY ) ;
105 long rc = ::sysconf( _SC_OPEN_MAX ) ;
107 n =
static_cast<int>( rc ) ;
109 for(
int fd = 0 ; fd < n ; fd++ )
111 if( fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO && fd != fd_keep )
114 ProcessImp::noCloseOnExec( STDIN_FILENO ) ;
115 ProcessImp::noCloseOnExec( STDOUT_FILENO ) ;
116 ProcessImp::noCloseOnExec( STDERR_FILENO ) ;
138 char * p = std::strerror( errno_ ) ;
139 std::string s( p ? p :
"" ) ;
140 if( s.empty() ) s =
"unknown error" ;
146 ProcessImp::beSpecial( special_identity , change_group ) ;
151 ProcessImp::beSpecialForExit(
SignalSafe() , special_identity ) ;
157 Identity ordinary_id( ordinary_name ) ;
162 if( change_group && !ordinary_id.
isRoot() )
166 ProcessImp::revokeExtraGroups() ;
169 Identity startup_id = ProcessImp::beOrdinary( ordinary_id , change_group ) ;
170 return { ordinary_id , startup_id } ;
175 ProcessImp::beOrdinary( ordinary_id , change_group ) ;
180 ProcessImp::beOrdinaryForExec( run_as_id ) ;
186 G::ProcessImp::setEffectiveUser(
id ) ;
193 G::ProcessImp::setEffectiveGroup(
id ) ;
201 for( std::size_t n : sizes )
203 std::vector<char> buffer( n ) ;
204 char * p = getcwd( &buffer[0] , buffer.size() ) ;
205 int error = errno_() ;
208 buffer.push_back(
'\0' ) ;
209 result.assign( &buffer[0] ) ;
212 else if( error != ERANGE )
217 if( result.empty() && !no_throw )
218 throw GetCwdError() ;
227 std::vector<char> buffer( std::max(100,PROC_PIDPATHINFO_MAXSIZE) ) ;
229 int rc = proc_pidpath( getpid() , &buffer[0] , buffer.size() ) ;
232 std::size_t n =
static_cast<std::size_t
>(rc) ;
233 if( n > buffer.size() ) n = buffer.size() ;
234 return std::string( &buffer[0] , n ) ;
238 return std::string() ;
246 ProcessImp::readlink_(
"/proc/self/exe" , result ) ||
247 ProcessImp::readlink_(
"/proc/curproc/file" , result ) ||
248 ProcessImp::readlink_(
"/proc/curproc/exe" , result ) ;
255G::Process::Id::Id() noexcept :
260std::string G::Process::Id::str()
const
262 std::ostringstream ss ;
268bool G::Process::Id::operator==(
const Id & other )
const noexcept
270 return m_pid == other.m_pid ;
274bool G::Process::Id::operator!=(
const Id & other )
const noexcept
276 return m_pid != other.m_pid ;
281void G::Process::UmaskImp::set( mode_t mode )
noexcept
283 GDEF_IGNORE_RETURN ::umask( mode ) ;
286mode_t G::Process::UmaskImp::set( Umask::Mode mode )
noexcept
288 mode_t old = ::umask( 2 ) ;
290 if( mode == Umask::Mode::Tightest )
292 else if( mode == Umask::Mode::Tighter )
294 else if( mode == Umask::Mode::Readable )
296 else if( mode == Umask::Mode::GroupOpen )
298 else if( mode == Umask::Mode::TightenOther )
299 new_ = old | mode_t(007) ;
300 else if( mode == Umask::Mode::LoosenGroup )
301 new_ = old & ~mode_t(070) ;
302 else if( mode == Umask::Mode::Open )
308G::Process::Umask::Umask( Mode mode ) :
309 m_imp(
std::make_unique<UmaskImp>())
311 m_imp->m_old_mode = UmaskImp::set( mode ) ;
314G::Process::Umask::~Umask()
316 UmaskImp::set( m_imp->m_old_mode ) ;
319void G::Process::Umask::set( Mode mode )
321 UmaskImp::set( mode ) ;
325void G::Process::Umask::tightenOther()
327 set( Mode::TightenOther ) ;
332void G::Process::Umask::loosenGroup()
334 set( Mode::LoosenGroup ) ;
340void G::ProcessImp::noCloseOnExec(
int fd )
noexcept
342 ::fcntl( fd , F_SETFD , 0 ) ;
345void G::ProcessImp::reopen(
int fd ,
int mode )
348 if( fd_null < 0 )
throw DevNullError() ;
349 ::dup2( fd_null , fd ) ;
354mode_t G::ProcessImp::umaskValue( Process::Umask::Mode mode )
357 if( mode == Process::Umask::Mode::Tightest ) m = 0177 ;
358 if( mode == Process::Umask::Mode::Tighter ) m = 0117 ;
359 if( mode == Process::Umask::Mode::Readable ) m = 0133 ;
360 if( mode == Process::Umask::Mode::GroupOpen ) m = 0113 ;
365bool G::ProcessImp::readlink_( string_view path , std::string & value )
368 if( !target.empty() ) value = target.str() ;
369 return !target.empty() ;
374G::Identity G::ProcessImp::beOrdinary( Identity nobody_id ,
bool change_group )
378 if( real_id.isRoot() )
386 if( !setEffectiveGroup( nobody_id , std::nothrow ) )
388 setEffectiveUser( old_id , std::nothrow ) ;
392 if( !setEffectiveUser( nobody_id , std::nothrow ) )
398 if( !setEffectiveUser( real_id , std::nothrow ) )
401 if( change_group && !setEffectiveGroup( real_id , std::nothrow ) )
403 setEffectiveUser( old_id , std::nothrow ) ;
410void G::ProcessImp::beOrdinaryForExec( Identity run_as_id )
noexcept
415 setRealGroup( run_as_id , std::nothrow ) ;
416 setEffectiveGroup( run_as_id , std::nothrow ) ;
417 setRealUser( run_as_id , std::nothrow ) ;
418 setEffectiveUser( run_as_id , std::nothrow ) ;
422void G::ProcessImp::beSpecial( Identity special_identity ,
bool change_group )
424 setEffectiveUser( special_identity ) ;
426 setEffectiveGroup( special_identity ) ;
429void G::ProcessImp::beSpecialForExit( SignalSafe , Identity special_identity )
noexcept
432 setEffectiveUser( special_identity , std::nothrow ) ;
433 setEffectiveGroup( special_identity , std::nothrow ) ;
436void G::ProcessImp::revokeExtraGroups()
442 GDEF_IGNORE_RETURN ::setgroups( 0U , &dummy ) ;
446bool G::ProcessImp::setRealUser( Identity
id , std::nothrow_t )
noexcept
448 return 0 == ::setuid(
id.userid() ) ;
451void G::ProcessImp::setEffectiveUser( Identity
id )
453 if( ::seteuid(
id.userid()) )
460bool G::ProcessImp::setEffectiveUser( Identity
id , std::nothrow_t )
noexcept
462 return 0 == ::seteuid(
id.userid() ) ;
465bool G::ProcessImp::setRealGroup( Identity
id , std::nothrow_t )
noexcept
467 return 0 == ::setgid(
id.groupid() ) ;
470void G::ProcessImp::setEffectiveGroup( Identity
id )
472 if( ::setegid(
id.groupid()) )
479bool G::ProcessImp::setEffectiveGroup( Identity
id , std::nothrow_t )
noexcept
481 return 0 == ::setegid(
id.groupid() ) ;
484void G::ProcessImp::throwError()
487 G_ERROR(
"G::ProcessImp::throwError: failed to change process identity" ) ;
488 throw IdentityError() ;
static G::Path readlink(const Path &link)
Reads a symlink. Throws on error.
A combination of user-id and group-id, with a very low-level interface to the get/set/e/uid/gid funct...
bool isRoot() const noexcept
Returns true if the userid is zero.
static Identity invalid() noexcept
Returns an invalid identity.
static Identity root() noexcept
Returns the superuser identity.
static Identity effective() noexcept
Returns the current effective identity.
static Identity real() noexcept
Returns the calling process's real identity.
A Path object represents a file system path.
const char * cstr() const noexcept
Returns the path string.
static Path nullDevice()
Returns the path of the "/dev/null" special file, or equivalent.
std::string str() const
Returns the path string.
static void beSpecialForExit(SignalSafe, Identity special_id) noexcept
A signal-safe version of beSpecial() that should only be used just before process exit.
static void closeOtherFiles(int fd_keep=-1)
Closes all open file descriptors except the three standard ones and possibly one other.
static std::pair< Identity, Identity > beOrdinaryAtStartup(const std::string &nobody, bool change_group)
Revokes special privileges (root or suid) at startup, possibly including extra group membership,...
static void beSpecial(Identity special_id, bool change_group=true)
Re-acquires special privileges (either root or suid).
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
static void beOrdinaryForExec(Identity run_as_id) noexcept
Sets the real and effective user-id and group-ids to those given, on a best-effort basis.
static void setEffectiveGroup(Identity)
Sets the effective group-id. Throws on error.
static std::string cwd(bool no_throw=false)
Returns the current working directory.
static void closeStderr()
Closes stderr and reopens it to the null device.
static void beOrdinary(Identity ordinary_id, bool change_group)
Releases special privileges.
static std::string exe()
Returns the absolute path of the current executable, independent of the argv array passed to main().
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
static void setEffectiveUser(Identity)
Sets the effective user-id. Throws on error.
static void closeFiles(bool keep_stderr=false)
Closes all open file descriptors and reopen stdin, stdout and possibly stderr to the null device.
static void cd(const Path &dir)
Changes directory.
An empty structure that is used to indicate a signal-safe, reentrant implementation.
static bool isPrintableAscii(string_view s) noexcept
Returns true if every character is a 7-bit, non-control character (ie.
static std::string lower(string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
constexpr const char * tx(const char *p)
A briefer alternative to G::gettext_noop().
A set of compile-time buffer sizes.