Instance.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 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 "debug/Messages.h"
12 #include "fs_io.h"
13 #include "Instance.h"
14 #include "md5.h"
15 #include "parser/Tokenizer.h"
16 #include "sbuf/Stream.h"
17 #include "SquidConfig.h"
18 #include "tools.h"
19 
20 #include <cerrno>
21 
22 /* To support concurrent PID files, convert local statics into PidFile class */
23 
26 static SBuf TheFile;
27 
30 static SBuf
32 {
33  if (!Config.pidFilename || strcmp(Config.pidFilename, "none") == 0)
34  return SBuf();
35 
36  // If chroot has been requested, then we first read the PID file before
37  // chroot() and then create/update it inside a chrooted environment.
38  // TODO: Consider removing half-baked chroot support from Squid.
39  extern bool Chrooted;
40  if (!Config.chroot_dir || Chrooted) // no need to compensate
41  return SBuf(Config.pidFilename);
42 
43  SBuf filename;
44  filename.append(Config.chroot_dir);
45  filename.append("/");
46  filename.append(Config.pidFilename);
47  debugs(50, 3, "outside chroot: " << filename);
48  return filename;
49 }
50 
52 static SBuf
53 PidFileDescription(const SBuf &filename)
54 {
55  return ToSBuf("PID file (", filename, ')');
56 }
57 
60 static SBuf
62 {
63  const auto name = PidFilenameCalc();
65  return name;
66 }
67 
69 static pid_t
70 GetOtherPid(File &pidFile)
71 {
72  const auto input = pidFile.readSmall(1, 32);
73  int64_t rawPid = -1;
74 
75  Parser::Tokenizer tok(input);
76  if (!(tok.int64(rawPid, 10, false) && // PID digits
77  (tok.skipOne(CharacterSet::CR)||true) && // optional CR (Windows/etc.)
78  tok.skipOne(CharacterSet::LF) && // required end of line
79  tok.atEnd())) { // no trailing garbage
80  throw TexcHere(ToSBuf("Malformed ", TheFile));
81  }
82 
83  debugs(50, 7, "found PID " << rawPid << " in " << TheFile);
84 
85  if (rawPid < 1)
86  throw TexcHere(ToSBuf("Bad ", TheFile, " contains unreasonably small PID value: ", rawPid));
87  const auto finalPid = static_cast<pid_t>(rawPid);
88  if (static_cast<int64_t>(finalPid) != rawPid)
89  throw TexcHere(ToSBuf("Bad ", TheFile, " contains unreasonably large PID value: ", rawPid));
90 
91  return finalPid;
92 }
93 
95 static bool
96 ProcessIsRunning(const pid_t pid)
97 {
98  const auto result = kill(pid, 0);
99  const auto savedErrno = errno;
100  if (result != 0)
101  debugs(50, 3, "kill(" << pid << ", 0) failed: " << xstrerr(savedErrno));
102  // if we do not have permissions to signal the process, then it is running
103  return (result == 0 || savedErrno == EPERM);
104 }
105 
107 static void
109 {
110  bool running = false;
111  SBuf description;
112  try {
113  const auto pid = GetOtherPid(pidFile);
114  description = ToSBuf(TheFile, " with PID ", pid);
115  running = ProcessIsRunning(pid);
116  }
117  catch (const std::exception &ex) {
118  debugs(50, 5, "assuming no other Squid instance: " << ex.what());
119  return;
120  }
121 
122  if (running)
123  throw TexcHere(ToSBuf("Squid is already running: Found fresh instance ", description));
124 
125  debugs(50, 5, "assuming stale instance " << description);
126 }
127 
128 pid_t
130 {
131  const auto filename = PidFilename();
132  if (filename.isEmpty())
133  throw TexcHere("no pid_filename configured");
134 
135  File pidFile(filename, File::Be::ReadOnly().locked());
136  return GetOtherPid(pidFile);
137 }
138 
139 void
141 {
142  const auto filename = PidFilename();
143  if (filename.isEmpty())
144  return; // the check is impossible
145 
146  if (const auto filePtr = File::Optional(filename, File::Be::ReadOnly().locked())) {
147  const std::unique_ptr<File> pidFile(filePtr);
148  ThrowIfAlreadyRunningWith(*pidFile);
149  } else {
150  // It is best to assume then to check because checking without a lock
151  // might lead to false positives that lead to no Squid starting at all!
152  debugs(50, 5, "cannot lock " << TheFile << "; assuming no other Squid is running");
153  // If our assumption is false, we will fail to _create_ the PID file,
154  // and, hence, will not start, allowing that other Squid to run.
155  }
156 }
157 
160 
162 static void
164 {
165  if (ThePidFileToRemove.isEmpty()) // not the PidFilename()!
166  return; // nothing to do
167 
168  debugs(50, Important(22), "Removing " << PidFileDescription(ThePidFileToRemove));
169 
170  // Do not write to cache_log after our PID file is removed because another
171  // instance may already be logging there. Stop logging now because, if we
172  // wait until safeunlink(), some debugs() may slip through into the now
173  // "unlocked" cache_log, especially if we avoid the sensitive suid() area.
174  // Use stderr to capture late debugs() that did not make it into cache_log.
176 
177  const char *filename = ThePidFileToRemove.c_str(); // avoid complex operations inside enter_suid()
178  enter_suid();
179  safeunlink(filename, 0);
180  leave_suid();
181 
183 }
184 
186 void
188 {
189  // Instance code assumes that we do not support PID filename reconfiguration
190  static bool called = false;
191  Must(!called);
192  called = true;
193 
194  const auto filename = PidFilename();
195  if (filename.isEmpty())
196  return; // nothing to do
197 
198  File pidFile(filename, File::Be::ReadWrite().locked().createdIfMissing().openedByRoot());
199 
200  // another instance may have started after the caller checked (if it did)
201  ThrowIfAlreadyRunningWith(pidFile);
202 
203  /* now we know that we own the PID file created and/or locked above */
204 
205  // Cleanup is scheduled through atexit() to ensure both:
206  // - cleanup upon fatal() and similar "unplanned" exits and
207  // - enter_suid() existence and proper logging support during cleanup.
208  // Even without PID filename reconfiguration support, we have to remember
209  // the file name we have used because Config.pidFilename may change!
210  (void)std::atexit(&RemoveInstance); // failures leave the PID file on disk
211  ThePidFileToRemove = filename;
212 
213  /* write our PID to the locked file */
214  SBuf pidBuf;
215  pidBuf.Printf("%d\n", static_cast<int>(getpid()));
216  pidFile.truncate();
217  pidFile.writeAll(pidBuf);
218 
219  // We must fsync before releasing the lock or other Squid processes may not see
220  // our written PID (and decide that they are dealing with a corrupted PID file).
221  pidFile.synchronize();
222 
223  debugs(50, Important(23), "Created " << TheFile);
224 }
225 
230 static SBuf
232 {
233  uint8_t hash[SQUID_MD5_DIGEST_LENGTH];
234 
235  SquidMD5_CTX ctx;
236  SquidMD5Init(&ctx);
237  const auto name = PidFilenameCalc();
238  SquidMD5Update(&ctx, name.rawContent(), name.length());
239  SquidMD5Final(hash, &ctx);
240 
241  // converts raw hash byte at a given position to a filename-suitable character
242  const auto hashAt = [&hash](const size_t idx) {
243  const auto safeChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
244  return safeChars[hash[idx] % strlen(safeChars)];
245  };
246 
247  SBuf buf;
248  buf.appendf("%c%c%c%c", hashAt(0), hashAt(1), hashAt(2), hashAt(3));
249  return buf;
250 }
251 
252 SBuf
253 Instance::NamePrefix(const char * const head, const char * const tail)
254 {
255  SBuf buf(head);
256  buf.append(service_name);
257  buf.append("-");
258  buf.append(PidFilenameHash());
259  if (tail) {
260  // TODO: Remove leading "-" from callers and explicitly add it here.
261  buf.append(tail);
262  }
263  return buf;
264 }
265 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
static pid_t GetOtherPid(File &pidFile)
Definition: Instance.cc:70
SQUIDCEXTERN void SquidMD5Init(struct SquidMD5Context *context)
Definition: md5.c:73
static SBuf ThePidFileToRemove
ties Instance::WriteOurPid() scheduler and RemoveInstance(void) handler
Definition: Instance.cc:159
static FileOpeningConfig ReadOnly()
Definition: File.cc:32
bool isEmpty() const
Definition: SBuf.h:435
Definition: SBuf.h:93
SBuf service_name(APP_SHORTNAME)
static File * Optional(const SBuf &aName, const FileOpeningConfig &cfg)
Definition: File.cc:124
static const CharacterSet LF
Definition: CharacterSet.h:92
static SBuf PidFileDescription(const SBuf &filename)
Definition: Instance.cc:53
SQUIDCEXTERN void SquidMD5Final(uint8_t digest[16], struct SquidMD5Context *context)
static bool ProcessIsRunning(const pid_t pid)
determines whether a given process is running at the time of the call
Definition: Instance.cc:96
static SBuf PidFilename()
Definition: Instance.cc:61
void clear()
Definition: SBuf.cc:175
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:63
a portable locking-aware exception-friendly file (with RAII API)
Definition: File.h:66
bool Chrooted
Definition: main.cc:1024
void synchronize()
fsync(2)
Definition: File.cc:304
static pid_t pid
Definition: IcmpSquid.cc:34
static const CharacterSet CR
Definition: CharacterSet.h:80
static SBuf PidFilenameHash()
Definition: Instance.cc:231
pid_t Other()
Definition: Instance.cc:129
void leave_suid(void)
Definition: tools.cc:559
#define SQUID_MD5_DIGEST_LENGTH
Definition: md5.h:66
SBuf & Printf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition: SBuf.cc:214
char * chroot_dir
Definition: SquidConfig.h:473
void WriteOurPid()
creates a PID file; throws on error
Definition: Instance.cc:187
SBuf readSmall(SBuf::size_type minBytes, SBuf::size_type maxBytes)
read(2) for small files
Definition: File.cc:241
SBuf NamePrefix(const char *head, const char *tail=nullptr)
Definition: Instance.cc:253
static SBuf TheFile
Definition: Instance.cc:26
const char * c_str()
Definition: SBuf.cc:516
static hash_table * hash
Definition: text_backend.cc:41
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
void writeAll(const SBuf &data)
write(2) with a "wrote everything" check
Definition: File.cc:280
void truncate()
makes the file size (and the current I/O offset) zero
Definition: File.cc:215
char * pidFilename
Definition: SquidConfig.h:224
static void RemoveInstance()
atexit() handler; removes the PID file created with Instance::WriteOurPid()
Definition: Instance.cc:163
Definition: parse.c:160
squidaio_request_t * head
Definition: aiops.cc:127
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
#define Must(condition)
Definition: TextException.h:75
#define Important(id)
Definition: Messages.h:93
void enter_suid(void)
Definition: tools.cc:623
void safeunlink(const char *s, int quiet)
Definition: fs_io.cc:433
static SBuf PidFilenameCalc()
Definition: Instance.cc:31
SQUIDCEXTERN void SquidMD5Update(struct SquidMD5Context *context, const void *buf, unsigned len)
Definition: md5.c:89
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition: SBuf.cc:229
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
static void ThrowIfAlreadyRunningWith(File &pidFile)
quits if another Squid instance (that owns the given PID file) is running
Definition: Instance.cc:108
void ThrowIfAlreadyRunning()
Definition: Instance.cc:140
static FileOpeningConfig ReadWrite()
Definition: File.cc:57
class SquidConfig Config
Definition: SquidConfig.cc:12
static void StopCacheLogUse()
Definition: debug.cc:1132

 

Introduction

Documentation

Support

Miscellaneous