Squid Web Cache master
Loading...
Searching...
No Matches
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 static variables into PidFile class */
23
27
30static 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
52static SBuf
53PidFileDescription(const SBuf &filename)
54{
55 return ToSBuf("PID file (", filename, ')');
56}
57
60static SBuf
62{
63 const auto name = PidFilenameCalc();
65 return name;
66}
67
69static pid_t
71{
72 const auto input = pidFile.readSmall(1, 32);
73 int64_t rawPid = -1;
74
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
95static bool
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
107static 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
128pid_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
139void
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);
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
162static 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
186void
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)
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
230static SBuf
232{
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
252SBuf
253Instance::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
static pid_t pid
Definition IcmpSquid.cc:36
static SBuf PidFilenameHash()
Definition Instance.cc:231
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 PidFilenameCalc()
Definition Instance.cc:31
static pid_t GetOtherPid(File &pidFile)
Definition Instance.cc:70
static void ThrowIfAlreadyRunningWith(File &pidFile)
quits if another Squid instance (that owns the given PID file) is running
Definition Instance.cc:108
static void RemoveInstance()
atexit() handler; removes the PID file created with Instance::WriteOurPid()
Definition Instance.cc:163
static SBuf PidFilename()
Definition Instance.cc:61
static SBuf ThePidFileToRemove
ties Instance::WriteOurPid() scheduler and RemoveInstance(void) handler
Definition Instance.cc:159
static SBuf TheFile
Definition Instance.cc:26
static SBuf PidFileDescription(const SBuf &filename)
Definition Instance.cc:53
class SquidConfig Config
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
#define Must(condition)
squidaio_request_t * head
Definition aiops.cc:129
static const CharacterSet LF
static const CharacterSet CR
static void StopCacheLogUse()
Definition debug.cc:1132
static FileOpeningConfig ReadWrite()
Definition File.cc:56
static FileOpeningConfig ReadOnly()
Definition File.cc:31
a portable locking-aware exception-friendly file (with RAII API)
Definition File.h:67
void writeAll(const SBuf &data)
write(2) with a "wrote everything" check
Definition File.cc:279
static File * Optional(const SBuf &aName, const FileOpeningConfig &cfg)
Definition File.cc:123
void truncate()
makes the file size (and the current I/O offset) zero
Definition File.cc:214
void synchronize()
fsync(2)
Definition File.cc:303
SBuf readSmall(SBuf::size_type minBytes, SBuf::size_type maxBytes)
read(2) for small files
Definition File.cc:240
Definition SBuf.h:94
const char * c_str()
Definition SBuf.cc:516
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition SBuf.cc:229
SBuf & Printf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition SBuf.cc:214
bool isEmpty() const
Definition SBuf.h:435
SBuf & append(const SBuf &S)
Definition SBuf.cc:185
void clear()
Definition SBuf.cc:175
char * chroot_dir
char * pidFilename
#define Important(id)
Definition Messages.h:93
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
void safeunlink(const char *s, int quiet)
Definition fs_io.cc:432
bool Chrooted
Definition main.cc:1004
SQUIDCEXTERN void SquidMD5Init(struct SquidMD5Context *context)
Definition md5.c:73
#define SQUID_MD5_DIGEST_LENGTH
Definition md5.h:66
SQUIDCEXTERN void SquidMD5Update(struct SquidMD5Context *context, const void *buf, unsigned len)
Definition md5.c:89
SQUIDCEXTERN void SquidMD5Final(uint8_t digest[16], struct SquidMD5Context *context)
void WriteOurPid()
creates a PID file; throws on error
Definition Instance.cc:187
void ThrowIfAlreadyRunning()
Definition Instance.cc:140
pid_t Other()
Definition Instance.cc:129
SBuf NamePrefix(const char *head, const char *tail=nullptr)
Definition Instance.cc:253
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63
Definition parse.c:160
SBuf service_name(APP_SHORTNAME)
static hash_table * hash
void leave_suid(void)
Definition tools.cc:560
void enter_suid(void)
Definition tools.cc:624
const char * xstrerr(int error)
Definition xstrerror.cc:83