]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/Instance.cc
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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.
10 #include "base/File.h"
11 #include "debug/Messages.h"
15 #include "parser/Tokenizer.h"
16 #include "sbuf/Stream.h"
17 #include "SquidConfig.h"
22 /* To support concurrent PID files, convert local statics into PidFile class */
24 /// Describes the (last) instance PID file being processed.
25 /// This hack shortens reporting code while keeping its messages consistent.
28 /// PidFilename() helper
29 /// \returns PID file name or, if PID signaling was disabled, an empty SBuf
33 if (!Config
.pidFilename
|| strcmp(Config
.pidFilename
, "none") == 0)
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.
40 if (!Config
.chroot_dir
|| Chrooted
) // no need to compensate
41 return SBuf(Config
.pidFilename
);
44 filename
.append(Config
.chroot_dir
);
46 filename
.append(Config
.pidFilename
);
47 debugs(50, 3, "outside chroot: " << filename
);
51 /// \returns PID file description for debugging messages and error reporting
53 PidFileDescription(const SBuf
&filename
)
55 return ToSBuf("PID file (", filename
, ')');
58 /// Instance entry points are expected to call this first.
59 /// \returns PidFilenameCalc() result while updating TheFile context
63 const auto name
= PidFilenameCalc();
64 TheFile
= PidFileDescription(name
);
68 /// \returns the PID of another Squid instance (or throws)
70 GetOtherPid(File
&pidFile
)
72 const auto input
= pidFile
.readSmall(1, 32);
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
));
83 debugs(50, 7, "found PID " << rawPid
<< " in " << TheFile
);
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
));
94 /// determines whether a given process is running at the time of the call
96 ProcessIsRunning(const pid_t pid
)
98 const auto result
= kill(pid
, 0);
99 const auto savedErrno
= errno
;
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
);
106 /// quits if another Squid instance (that owns the given PID file) is running
108 ThrowIfAlreadyRunningWith(File
&pidFile
)
110 bool running
= false;
113 const auto pid
= GetOtherPid(pidFile
);
114 description
= ToSBuf(TheFile
, " with PID ", pid
);
115 running
= ProcessIsRunning(pid
);
117 catch (const std::exception
&ex
) {
118 debugs(50, 5, "assuming no other Squid instance: " << ex
.what());
123 throw TexcHere(ToSBuf("Squid is already running: Found fresh instance ", description
));
125 debugs(50, 5, "assuming stale instance " << description
);
131 const auto filename
= PidFilename();
132 if (filename
.isEmpty())
133 throw TexcHere("no pid_filename configured");
135 File
pidFile(filename
, File::Be::ReadOnly().locked());
136 return GetOtherPid(pidFile
);
140 Instance::ThrowIfAlreadyRunning()
142 const auto filename
= PidFilename();
143 if (filename
.isEmpty())
144 return; // the check is impossible
146 if (const auto filePtr
= File::Optional(filename
, File::Be::ReadOnly().locked())) {
147 const std::unique_ptr
<File
> pidFile(filePtr
);
148 ThrowIfAlreadyRunningWith(*pidFile
);
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.
158 /// ties Instance::WriteOurPid() scheduler and RemoveInstance(void) handler
159 static SBuf ThePidFileToRemove
;
161 /// atexit() handler; removes the PID file created with Instance::WriteOurPid()
165 if (ThePidFileToRemove
.isEmpty()) // not the PidFilename()!
166 return; // nothing to do
168 debugs(50, Important(22), "Removing " << PidFileDescription(ThePidFileToRemove
));
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.
175 Debug::StopCacheLogUse();
177 const char *filename
= ThePidFileToRemove
.c_str(); // avoid complex operations inside enter_suid()
179 safeunlink(filename
, 0);
182 ThePidFileToRemove
.clear();
185 /// creates a PID file; throws on error
187 Instance::WriteOurPid()
189 // Instance code assumes that we do not support PID filename reconfiguration
190 static bool called
= false;
194 const auto filename
= PidFilename();
195 if (filename
.isEmpty())
196 return; // nothing to do
198 File
pidFile(filename
, File::Be::ReadWrite().locked().createdIfMissing().openedByRoot());
200 // another instance may have started after the caller checked (if it did)
201 ThrowIfAlreadyRunningWith(pidFile
);
203 /* now we know that we own the PID file created and/or locked above */
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
;
213 /* write our PID to the locked file */
215 pidBuf
.Printf("%d\n", static_cast<int>(getpid()));
217 pidFile
.writeAll(pidBuf
);
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();
223 debugs(50, Important(23), "Created " << TheFile
);
226 /// A hash that is likely to be unique across instances running on the same host
227 /// because such concurrent instances should use unique PID filenames.
228 /// All instances with disabled PID file maintenance have the same hash value.
229 /// \returns a 4-character string suitable for use in file names and similar contexts
233 uint8_t hash
[SQUID_MD5_DIGEST_LENGTH
];
237 const auto name
= PidFilenameCalc();
238 SquidMD5Update(&ctx
, name
.rawContent(), name
.length());
239 SquidMD5Final(hash
, &ctx
);
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
)];
248 buf
.appendf("%c%c%c%c", hashAt(0), hashAt(1), hashAt(2), hashAt(3));
253 Instance::NamePrefix(const char * const head
, const char * const tail
)
256 buf
.append(service_name
);
258 buf
.append(PidFilenameHash());
260 // TODO: Remove leading "-" from callers and explicitly add it here.