E-MailRelay
gpidfile.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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/// \file gpidfile.cpp
19///
20
21#include "gdef.h"
22#include "gpidfile.h"
23#include "gprocess.h"
24#include "groot.h"
25#include "gstr.h"
26#include "gcleanup.h"
27#include "gfile.h"
28#include "glog.h"
29#include <fstream>
30#include <string>
31#include <array>
32
34= default;
35
37{
38 if( valid() )
39 {
40 bool done = cleanup( SignalSafe() , m_path.cstr() ) ;
41 if( !done )
42 {
43 Root::atExit() ;
44 cleanup( SignalSafe() , m_path.cstr() ) ;
45 }
46 }
47}
48
49G::PidFile::PidFile( const Path & path ) :
50 m_path(path)
51{
52}
53
54void G::PidFile::init( const Path & path )
55{
56 m_path = path ;
57}
58
59void G::PidFile::create( const Path & pid_file )
60{
61 if( !pid_file.empty() )
62 {
63 // (the effective user-id and umask is set by the caller)
64 std::ofstream file ;
65 File::open( file , pid_file , File::Text() ) ;
66 int e = G::Process::errno_() ;
67 if( !file.good() )
68 throw Error( "cannot create file" , pid_file.str() , G::Process::strerror(e) ) ;
69 Process::Id pid ;
70 file << pid.str() << std::endl ;
71 file.close() ;
72 if( file.fail() )
73 throw Error( "cannot write file" , pid_file.str() ) ;
74
75 // (leak only if necessary)
76 static constexpr std::size_t buffer_size = 60U ; // eg. "/var/run/whatever/whatever.pid"
77 static std::array<char,buffer_size> buffer ;
78 const char * cleanup_arg = &buffer[0] ;
79 if( buffer[0] == '\0' && pid_file.size() < buffer.size() )
80 G::Str::strncpy_s( &buffer[0] , buffer.size() , pid_file.cstr() , pid_file.size() ) ;
81 else
82 cleanup_arg = Cleanup::strdup( pid_file.str() ) ;
83
84 Cleanup::add( cleanup , cleanup_arg ) ;
85 }
86}
87
88G::Process::Id G::PidFile::read( SignalSafe , const char * path ) noexcept
89{
90 int fd = File::open( path , File::InOutAppend::In ) ;
91 if( fd < 0 )
92 return Process::Id::invalid() ;
93
94 constexpr std::size_t buffer_size = 11U ;
95 std::array<char,buffer_size> buffer ; // NOLINT cppcoreguidelines-pro-type-member-init
96 buffer[0U] = '\0' ;
97
98 ssize_t rc = File::read( fd , &buffer[0] , buffer_size-1U ) ;
99 File::close( fd ) ;
100 if( rc <= 0 )
101 return Process::Id::invalid() ;
102
103 return Process::Id( &buffer[0] , &buffer[0]+static_cast<std::size_t>(rc) ) ;
104}
105
106bool G::PidFile::cleanup( SignalSafe safe , const char * path ) noexcept
107{
108 try
109 {
110 if( path == nullptr || *path == '\0' )
111 return true ; // nothing to do
112
113 Process::Id this_pid ;
114 Process::Id file_pid = read( safe , path ) ;
115 if( this_pid != file_pid )
116 return false ; // try again in case we didnt have read permission
117
118 return 0 == std::remove( path ) ;
119 }
120 catch(...)
121 {
122 return false ;
123 }
124}
125
127{
128 if( valid() && ! m_path.isAbsolute() )
129 throw Error(std::string("must be an absolute path: ")+m_path.str()) ;
130}
131
133{
134 if( valid() )
135 {
136 create( m_path ) ;
137 m_committed = true ;
138 }
139}
140
142{
143 return m_committed ;
144}
145
147{
148 return m_path ;
149}
150
151bool G::PidFile::valid() const
152{
153 return !m_path.empty() ;
154}
155
static const char * strdup(const char *)
A strdup() function that makes it clear in the stack trace that leaks are expected.
static void add(bool(*fn)(SignalSafe, const char *), const char *arg)
Adds the given handler to the list of handlers that are to be called when the process terminates abno...
An overload discriminator for G::File::open().
Definition: gfile.h:62
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
Definition: gfile_unix.cpp:55
static void close(int fd) noexcept
Calls ::close() or equivalent.
Definition: gfile_unix.cpp:126
static ssize_t read(int fd, char *, std::size_t) noexcept
Calls ::read() or equivalent.
Definition: gfile_unix.cpp:116
A Path object represents a file system path.
Definition: gpath.h:72
const char * cstr() const noexcept
Returns the path string.
Definition: gpath.h:221
std::size_t size() const noexcept
Returns the length of the path string.
Definition: gpath.h:209
std::string str() const
Returns the path string.
Definition: gpath.h:215
bool empty() const noexcept
Returns true if size() is zero.
Definition: gpath.h:203
bool committed() const
Returns true if commit() has been called with a valid path().
Definition: gpidfile.cpp:141
void commit()
Creates the file and installs signal handlers to cleanup() the file on abnormal process termination.
Definition: gpidfile.cpp:132
PidFile()
Default constructor.
void check()
Throws an exception if the path is not absolute.
Definition: gpidfile.cpp:126
~PidFile()
Destructor. Calls cleanup() to delete the file.
Definition: gpidfile.cpp:36
Path path() const
Returns the path as supplied to the constructor or init().
Definition: gpidfile.cpp:146
static bool cleanup(SignalSafe, const char *path) noexcept
Deletes the specified pid file if it contains this process's id.
Definition: gpidfile.cpp:106
void init(const Path &pid_file_path)
Used after default construction to make the object active.
Definition: gpidfile.cpp:54
Process-id class.
Definition: gprocess.h:140
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
static void atExit() noexcept
Re-acquires special privileges just before process exit.
Definition: groot.cpp:68
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:37
static errno_t strncpy_s(char *dst, std::size_t n_dst, const char *src, std::size_t count) noexcept
Does the same as windows strncpy_s().
Definition: gstr.cpp:1528