Instance.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #include "squid.h"
10 #include "base/File.h"
11 #include "fs_io.h"
12 #include "Instance.h"
13 #include "parser/Tokenizer.h"
14 #include "sbuf/Stream.h"
15 #include "SquidConfig.h"
16 #include "tools.h"
17 
18 #include <cerrno>
19 
20 /* To support concurrent PID files, convert local statics into PidFile class */
21 
24 static SBuf TheFile;
25 
28 static SBuf
30 {
31  if (!Config.pidFilename || strcmp(Config.pidFilename, "none") == 0)
32  return SBuf();
33 
34  // If chroot has been requested, then we first read the PID file before
35  // chroot() and then create/update it inside a chrooted environment.
36  // TODO: Consider removing half-baked chroot support from Squid.
37  extern bool Chrooted;
38  if (!Config.chroot_dir || Chrooted) // no need to compensate
39  return SBuf(Config.pidFilename);
40 
41  SBuf filename;
42  filename.append(Config.chroot_dir);
43  filename.append("/");
44  filename.append(Config.pidFilename);
45  debugs(50, 3, "outside chroot: " << filename);
46  return filename;
47 }
48 
50 static SBuf
51 PidFileDescription(const SBuf &filename)
52 {
53  return ToSBuf("PID file (", filename, ')');
54 }
55 
58 static SBuf
60 {
61  const auto name = PidFilenameCalc();
62  TheFile = PidFileDescription(name);
63  return name;
64 }
65 
67 static pid_t
68 GetOtherPid(File &pidFile)
69 {
70  const auto input = pidFile.readSmall(1, 32);
71  int64_t rawPid = -1;
72 
73  Parser::Tokenizer tok(input);
74  if (!(tok.int64(rawPid, 10, false) && // PID digits
75  (tok.skipOne(CharacterSet::CR)||true) && // optional CR (Windows/etc.)
76  tok.skipOne(CharacterSet::LF) && // required end of line
77  tok.atEnd())) { // no trailing garbage
78  throw TexcHere(ToSBuf("Malformed ", TheFile));
79  }
80 
81  debugs(50, 7, "found PID " << rawPid << " in " << TheFile);
82 
83  if (rawPid <= 1)
84  throw TexcHere(ToSBuf("Bad ", TheFile, " contains unreasonably small PID value: ", rawPid));
85  const auto finalPid = static_cast<pid_t>(rawPid);
86  if (static_cast<int64_t>(finalPid) != rawPid)
87  throw TexcHere(ToSBuf("Bad ", TheFile, " contains unreasonably large PID value: ", rawPid));
88 
89  return finalPid;
90 }
91 
93 static bool
94 ProcessIsRunning(const pid_t pid)
95 {
96  const auto result = kill(pid, 0);
97  const auto savedErrno = errno;
98  if (result != 0)
99  debugs(50, 3, "kill(" << pid << ", 0) failed: " << xstrerr(savedErrno));
100  // if we do not have permissions to signal the process, then it is running
101  return (result == 0 || savedErrno == EPERM);
102 }
103 
105 static void
107 {
108  bool running = false;
109  SBuf description;
110  try {
111  const auto pid = GetOtherPid(pidFile);
112  description = ToSBuf(TheFile, " with PID ", pid);
113  running = ProcessIsRunning(pid);
114  }
115  catch (const std::exception &ex) {
116  debugs(50, 5, "assuming no other Squid instance: " << ex.what());
117  return;
118  }
119 
120  if (running)
121  throw TexcHere(ToSBuf("Squid is already running: Found fresh instance ", description));
122 
123  debugs(50, 5, "assuming stale instance " << description);
124 }
125 
126 pid_t
128 {
129  const auto filename = PidFilename();
130  if (filename.isEmpty())
131  throw TexcHere("no pid_filename configured");
132 
133  File pidFile(filename, File::Be::ReadOnly().locked());
134  return GetOtherPid(pidFile);
135 }
136 
137 void
139 {
140  const auto filename = PidFilename();
141  if (filename.isEmpty())
142  return; // the check is impossible
143 
144  if (const auto filePtr = File::Optional(filename, File::Be::ReadOnly().locked())) {
145  const std::unique_ptr<File> pidFile(filePtr);
146  ThrowIfAlreadyRunningWith(*pidFile);
147  } else {
148  // It is best to assume then to check because checking without a lock
149  // might lead to false positives that lead to no Squid starting at all!
150  debugs(50, 5, "cannot lock " << TheFile << "; assuming no other Squid is running");
151  // If our assumption is false, we will fail to _create_ the PID file,
152  // and, hence, will not start, allowing that other Squid to run.
153  }
154 }
155 
158 
160 static void
162 {
163  if (ThePidFileToRemove.isEmpty()) // not the PidFilename()!
164  return; // nothing to do
165 
166  debugs(50, DBG_IMPORTANT, "Removing " << PidFileDescription(ThePidFileToRemove));
167  const char *filename = ThePidFileToRemove.c_str(); // avoid complex operations inside enter_suid()
168  enter_suid();
169  safeunlink(filename, 0);
170  leave_suid();
171 
172  ThePidFileToRemove.clear();
173 }
174 
176 void
178 {
179  // Instance code assumes that we do not support PID filename reconfiguration
180  static bool called = false;
181  Must(!called);
182  called = true;
183 
184  const auto filename = PidFilename();
185  if (filename.isEmpty())
186  return; // nothing to do
187 
188  File pidFile(filename, File::Be::ReadWrite().locked().createdIfMissing().openedByRoot());
189 
190  // another instance may have started after the caller checked (if it did)
191  ThrowIfAlreadyRunningWith(pidFile);
192 
193  /* now we know that we own the PID file created and/or locked above */
194 
195  // Cleanup is scheduled through atexit() to ensure both:
196  // - cleanup upon fatal() and similar "unplanned" exits and
197  // - enter_suid() existence and proper logging support during cleanup.
198  // Even without PID filename reconfiguration support, we have to remember
199  // the file name we have used because Config.pidFilename may change!
200  (void)std::atexit(&RemoveInstance); // failures leave the PID file on disk
201  ThePidFileToRemove = filename;
202 
203  /* write our PID to the locked file */
204  SBuf pidBuf;
205  pidBuf.Printf("%d\n", static_cast<int>(getpid()));
206  pidFile.truncate();
207  pidFile.writeAll(pidBuf);
208 
209  // We must fsync before releasing the lock or other Squid processes may not see
210  // our written PID (and decide that they are dealing with a corrupted PID file).
211  pidFile.synchronize();
212 
213  debugs(50, DBG_IMPORTANT, "Created " << TheFile);
214 }
215 
static SBuf TheFile
Definition: Instance.cc:24
char * chroot_dir
Definition: SquidConfig.h:476
void truncate()
makes the file size (and the current I/O offset) zero
Definition: File.cc:214
static const CharacterSet LF
Definition: CharacterSet.h:89
static File * Optional(const SBuf &aName, const FileOpeningConfig &cfg)
Definition: File.cc:123
void safeunlink(const char *s, int quiet)
Definition: fs_io.cc:497
Definition: SBuf.h:87
char * pidFilename
Definition: SquidConfig.h:213
SBuf & append(const SBuf &S)
Definition: SBuf.cc:207
void clear()
Definition: SBuf.cc:190
bool isEmpty() const
Definition: SBuf.h:422
const char * xstrerr(int error)
Definition: xstrerror.cc:83
a portable locking-aware exception-friendly file (with RAII API)
Definition: File.h:66
static SBuf PidFilenameCalc()
Definition: Instance.cc:29
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
static bool ProcessIsRunning(const pid_t pid)
determines whether a given process is running at the time of the call
Definition: Instance.cc:94
#define DBG_IMPORTANT
Definition: Debug.h:45
static void ThrowIfAlreadyRunningWith(File &pidFile)
quits if another Squid instance (that owns the given PID file) is running
Definition: Instance.cc:106
SBuf & Printf(const char *fmt,...)
Definition: SBuf.cc:236
pid_t Other()
Definition: Instance.cc:127
void leave_suid(void)
Definition: tools.cc:504
static const CharacterSet CR
Definition: CharacterSet.h:77
bool atEnd() const
whether the end of the buffer has been reached
Definition: Tokenizer.h:41
const char * c_str()
Definition: SBuf.cc:546
static pid_t GetOtherPid(File &pidFile)
Definition: Instance.cc:68
void WriteOurPid()
creates a PID file; throws on error
Definition: Instance.cc:177
Definition: parse.c:160
bool skipOne(const CharacterSet &discardables)
Definition: Tokenizer.cc:132
static SBuf ThePidFileToRemove
ties Instance::WriteOurPid() scheduler and RemoveInstance(void) handler
Definition: Instance.cc:157
SBuf readSmall(SBuf::size_type minBytes, SBuf::size_type maxBytes)
read(2) for small files
Definition: File.cc:240
#define Must(cond)
Definition: TextException.h:89
static pid_t pid
Definition: IcmpSquid.cc:35
static void RemoveInstance()
atexit() handler; removes the PID file created with Instance::WriteOurPid()
Definition: Instance.cc:161
void enter_suid(void)
Definition: tools.cc:575
bool Chrooted
Definition: main.cc:1091
SBuf ToSBuf(Args &&...args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:124
static SBuf PidFileDescription(const SBuf &filename)
Definition: Instance.cc:51
static SBuf PidFilename()
Definition: Instance.cc:59
#define TexcHere(msg)
Definition: TextException.h:81
void writeAll(const SBuf &data)
write(2) with a "wrote everything" check
Definition: File.cc:278
void synchronize()
fsync(2)
Definition: File.cc:300
static FileOpeningConfig ReadWrite()
Definition: File.cc:56
class SquidConfig Config
Definition: SquidConfig.cc:12
static FileOpeningConfig ReadOnly()
Definition: File.cc:31
void ThrowIfAlreadyRunning()
Definition: Instance.cc:138
bool int64(int64_t &result, int base=0, bool allowSign=true, SBuf::size_type limit=SBuf::npos)
Definition: Tokenizer.cc:209

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors