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 enum class Mode { read_only , write_only } ;
45 void reopen(
int fd , Mode mode ) ;
46 mode_t umaskValue( G::Process::Umask::Mode mode ) ;
47 bool readlink_( std::string_view path , std::string & value ) ;
48 bool setRealUser( Identity
id , std::nothrow_t ) noexcept ;
49 bool setRealGroup( Identity
id , std::nothrow_t ) noexcept ;
50 void setEffectiveUser( Identity
id ) ;
51 bool setEffectiveUser( Identity
id , std::nothrow_t ) noexcept ;
52 void setEffectiveGroup( Identity
id ) ;
53 bool setEffectiveGroup( Identity
id , std::nothrow_t ) noexcept ;
55 void beSpecial( Identity special_identity ,
bool change_group ) ;
56 void beSpecialForExit( SignalSafe , Identity special_identity ) noexcept ;
57 Identity beOrdinaryAtStartup( Identity ,
bool change_group ) ;
58 Identity beOrdinary( Identity ,
bool change_group ) ;
59 void beOrdinaryForExec( Identity run_as_id ) noexcept ;
60 void revokeExtraGroups() ;
64class G::Process::UmaskImp
68 static mode_t set( Process::Umask::Mode ) noexcept ;
69 static void set( mode_t ) noexcept ;
76 if( !
cd(dir,std::nothrow) )
77 throw CannotChangeDirectory( dir.
str() ) ;
82 return 0 == ::chdir( dir.
cstr() ) ;
87 ProcessImp::reopen( STDERR_FILENO , ProcessImp::Mode::write_only ) ;
92 std::cout << std::flush ;
93 std::cerr << std::flush ;
95 ProcessImp::reopen( STDIN_FILENO , ProcessImp::Mode::read_only ) ;
96 ProcessImp::reopen( STDOUT_FILENO , ProcessImp::Mode::write_only ) ;
98 ProcessImp::reopen( STDERR_FILENO , ProcessImp::Mode::write_only ) ;
101 inheritStandardFiles() ;
107 long rc = ::sysconf( _SC_OPEN_MAX ) ;
109 n =
static_cast<int>( rc ) ;
111 for(
int fd = 0 ; fd < n ; fd++ )
113 if( fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO && fd != fd_keep )
120 ProcessImp::noCloseOnExec( STDIN_FILENO ) ;
121 ProcessImp::noCloseOnExec( STDOUT_FILENO ) ;
122 ProcessImp::noCloseOnExec( STDERR_FILENO ) ;
144 char * p = std::strerror( errno_ ) ;
145 std::string s( p ? p :
"" ) ;
146 if( s.empty() ) s =
"unknown error" ;
153 return std::string(
"error ").append( std::to_string(e) ) ;
159 ProcessImp::beSpecial( special_identity , change_group ) ;
164 ProcessImp::beSpecialForExit(
SignalSafe() , special_identity ) ;
170 Identity ordinary_id( ordinary_name ) ;
175 if( change_group && !ordinary_id.
isRoot() )
179 ProcessImp::revokeExtraGroups() ;
182 Identity startup_id = ProcessImp::beOrdinary( ordinary_id , change_group ) ;
183 return { ordinary_id , startup_id } ;
188 ProcessImp::beOrdinary( ordinary_id , change_group ) ;
193 ProcessImp::beOrdinaryForExec( run_as_id ) ;
199 G::ProcessImp::setEffectiveUser(
id ) ;
206 G::ProcessImp::setEffectiveGroup(
id ) ;
212 return cwdImp(
false ) ;
217 return cwdImp(
true ) ;
220G::Path G::Process::cwdImp(
bool no_throw )
224 for( std::size_t n : sizes )
226 std::vector<char> buffer( n ) ;
227 char * p = getcwd( buffer.data() , buffer.size() ) ;
228 int error = errno_() ;
231 buffer.push_back(
'\0' ) ;
232 result.assign( buffer.data() ) ;
235 else if( error != ERANGE )
240 if( result.empty() && !no_throw )
241 throw GetCwdError() ;
250 std::vector<char> buffer( std::max(100,PROC_PIDPATHINFO_MAXSIZE) ) ;
252 int rc = proc_pidpath( getpid() , buffer.data() , buffer.size() ) ;
255 std::size_t n =
static_cast<std::size_t
>(rc) ;
256 if( n > buffer.size() ) n = buffer.size() ;
257 return Path( std::string_view(buffer.data(),n) ) ;
269 ProcessImp::readlink_(
"/proc/self/exe" , result ) ||
270 ProcessImp::readlink_(
"/proc/curproc/file" , result ) ||
271 ProcessImp::readlink_(
"/proc/curproc/exe" , result ) ;
278G::Process::Id::Id() noexcept :
283std::string G::Process::Id::str()
const
285 std::ostringstream ss ;
291bool G::Process::Id::operator==(
const Id & other )
const noexcept
293 return m_pid == other.m_pid ;
297bool G::Process::Id::operator!=(
const Id & other )
const noexcept
299 return m_pid != other.m_pid ;
304void G::Process::UmaskImp::set( mode_t mode )
noexcept
306 GDEF_IGNORE_RETURN ::umask( mode ) ;
309mode_t G::Process::UmaskImp::set( Umask::Mode mode )
noexcept
311 mode_t old = ::umask( 2 ) ;
313 if( mode == Umask::Mode::Tightest )
315 else if( mode == Umask::Mode::Tighter )
317 else if( mode == Umask::Mode::Readable )
319 else if( mode == Umask::Mode::GroupOpen )
321 else if( mode == Umask::Mode::TightenOther )
322 new_ = old | mode_t(007) ;
323 else if( mode == Umask::Mode::LoosenGroup )
324 new_ = old & ~mode_t(070) ;
325 else if( mode == Umask::Mode::Open )
331G::Process::Umask::Umask( Mode mode ) :
332 m_imp(
std::make_unique<UmaskImp>())
334 m_imp->m_old_mode = UmaskImp::set( mode ) ;
337G::Process::Umask::~Umask()
339 UmaskImp::set( m_imp->m_old_mode ) ;
342void G::Process::Umask::set( Mode mode )
344 UmaskImp::set( mode ) ;
348void G::Process::Umask::tightenOther()
350 set( Mode::TightenOther ) ;
355void G::Process::Umask::loosenGroup()
357 set( Mode::LoosenGroup ) ;
363void G::ProcessImp::noCloseOnExec(
int fd )
noexcept
365 ::fcntl( fd , F_SETFD , 0 ) ;
368void G::ProcessImp::reopen(
int fd , Mode mode_in )
370 auto mode = mode_in == Mode::read_only ? File::InOutAppend::In : File::InOutAppend::OutNoCreate ;
372 if( fd_null < 0 )
throw DevNullError() ;
373 ::dup2( fd_null , fd ) ;
378mode_t G::ProcessImp::umaskValue( Process::Umask::Mode mode )
381 if( mode == Process::Umask::Mode::Tightest ) m = 0177 ;
382 if( mode == Process::Umask::Mode::Tighter ) m = 0117 ;
383 if( mode == Process::Umask::Mode::Readable ) m = 0133 ;
384 if( mode == Process::Umask::Mode::GroupOpen ) m = 0113 ;
389bool G::ProcessImp::readlink_( std::string_view path , std::string & value )
392 if( !target.empty() ) value = target.str() ;
393 return !target.empty() ;
398G::Identity G::ProcessImp::beOrdinary( Identity nobody_id ,
bool change_group )
402 if( real_id.isRoot() )
410 if( !setEffectiveGroup( nobody_id , std::nothrow ) )
412 setEffectiveUser( old_id , std::nothrow ) ;
416 if( !setEffectiveUser( nobody_id , std::nothrow ) )
422 if( !setEffectiveUser( real_id , std::nothrow ) )
425 if( change_group && !setEffectiveGroup( real_id , std::nothrow ) )
427 setEffectiveUser( old_id , std::nothrow ) ;
434void G::ProcessImp::beOrdinaryForExec( Identity run_as_id )
noexcept
439 setRealGroup( run_as_id , std::nothrow ) ;
440 setEffectiveGroup( run_as_id , std::nothrow ) ;
441 setRealUser( run_as_id , std::nothrow ) ;
442 setEffectiveUser( run_as_id , std::nothrow ) ;
446void G::ProcessImp::beSpecial( Identity special_identity ,
bool change_group )
448 setEffectiveUser( special_identity ) ;
450 setEffectiveGroup( special_identity ) ;
453void G::ProcessImp::beSpecialForExit( SignalSafe , Identity special_identity )
noexcept
456 setEffectiveUser( special_identity , std::nothrow ) ;
457 setEffectiveGroup( special_identity , std::nothrow ) ;
460void G::ProcessImp::revokeExtraGroups()
466 GDEF_IGNORE_RETURN ::setgroups( 0U , &dummy ) ;
470bool G::ProcessImp::setRealUser( Identity
id , std::nothrow_t )
noexcept
472 return 0 == ::setuid(
id.userid() ) ;
475void G::ProcessImp::setEffectiveUser( Identity
id )
477 if( ::seteuid(
id.userid()) )
484bool G::ProcessImp::setEffectiveUser( Identity
id , std::nothrow_t )
noexcept
486 return 0 == ::seteuid(
id.userid() ) ;
489bool G::ProcessImp::setRealGroup( Identity
id , std::nothrow_t )
noexcept
491 return 0 == ::setgid(
id.groupid() ) ;
494void G::ProcessImp::setEffectiveGroup( Identity
id )
496 if( ::setegid(
id.groupid()) )
503bool G::ProcessImp::setEffectiveGroup( Identity
id , std::nothrow_t )
noexcept
505 return 0 == ::setegid(
id.groupid() ) ;
508void G::ProcessImp::throwError()
511 G_ERROR(
"G::ProcessImp::throwError: failed to change process identity" ) ;
512 throw IdentityError() ;
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
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 value_type * cstr() const noexcept
Returns the path's c-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 optionally one other.
static Path cwd()
Returns the current working directory. Throws on error.
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 Path exe()
Returns the absolute path of the current executable, independent of the argv array passed to main().
static void setEffectiveGroup(Identity)
Sets the effective group-id. Throws on error.
static void closeStderr()
Closes stderr and reopens it to the null device.
static void inheritStandardFiles()
Makes sure that the standard file descriptors are inherited.
static void beOrdinary(Identity ordinary_id, bool change_group)
Releases special privileges.
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 reopens stdin, stdout and possibly stderr to the null device.
static void cd(const Path &dir)
Changes directory.
static std::string errorMessage(DWORD error)
Translates a GetLastError() value into a meaningful diagnostic string.
An empty structure that is used to indicate a signal-safe, reentrant implementation.
static std::string lower(std::string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
static bool isPrintableAscii(std::string_view s) noexcept
Returns true if every character is between 0x20 and 0x7e inclusive.
constexpr const char * tx(const char *p) noexcept
A briefer alternative to G::gettext_noop().
A set of compile-time buffer sizes.