]> git.ipfire.org Git - thirdparty/squid.git/blob - src/Instance.cc
Bug 5428: Warn if pkg-config is not found (#1902)
[thirdparty/squid.git] / src / Instance.cc
1 /*
2 * Copyright (C) 1996-2023 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 "parser/Tokenizer.h"
15 #include "sbuf/Stream.h"
16 #include "SquidConfig.h"
17 #include "tools.h"
18
19 #include <cerrno>
20
21 /* To support concurrent PID files, convert local statics into PidFile class */
22
23 /// Describes the (last) instance PID file being processed.
24 /// This hack shortens reporting code while keeping its messages consistent.
25 static SBuf TheFile;
26
27 /// PidFilename() helper
28 /// \returns PID file name or, if PID signaling was disabled, an empty SBuf
29 static SBuf
30 PidFilenameCalc()
31 {
32 if (!Config.pidFilename || strcmp(Config.pidFilename, "none") == 0)
33 return SBuf();
34
35 // If chroot has been requested, then we first read the PID file before
36 // chroot() and then create/update it inside a chrooted environment.
37 // TODO: Consider removing half-baked chroot support from Squid.
38 extern bool Chrooted;
39 if (!Config.chroot_dir || Chrooted) // no need to compensate
40 return SBuf(Config.pidFilename);
41
42 SBuf filename;
43 filename.append(Config.chroot_dir);
44 filename.append("/");
45 filename.append(Config.pidFilename);
46 debugs(50, 3, "outside chroot: " << filename);
47 return filename;
48 }
49
50 /// \returns PID file description for debugging messages and error reporting
51 static SBuf
52 PidFileDescription(const SBuf &filename)
53 {
54 return ToSBuf("PID file (", filename, ')');
55 }
56
57 /// Instance entry points are expected to call this first.
58 /// \returns PidFilenameCalc() result while updating TheFile context
59 static SBuf
60 PidFilename()
61 {
62 const auto name = PidFilenameCalc();
63 TheFile = PidFileDescription(name);
64 return name;
65 }
66
67 /// \returns the PID of another Squid instance (or throws)
68 static pid_t
69 GetOtherPid(File &pidFile)
70 {
71 const auto input = pidFile.readSmall(1, 32);
72 int64_t rawPid = -1;
73
74 Parser::Tokenizer tok(input);
75 if (!(tok.int64(rawPid, 10, false) && // PID digits
76 (tok.skipOne(CharacterSet::CR)||true) && // optional CR (Windows/etc.)
77 tok.skipOne(CharacterSet::LF) && // required end of line
78 tok.atEnd())) { // no trailing garbage
79 throw TexcHere(ToSBuf("Malformed ", TheFile));
80 }
81
82 debugs(50, 7, "found PID " << rawPid << " in " << TheFile);
83
84 if (rawPid < 1)
85 throw TexcHere(ToSBuf("Bad ", TheFile, " contains unreasonably small PID value: ", rawPid));
86 const auto finalPid = static_cast<pid_t>(rawPid);
87 if (static_cast<int64_t>(finalPid) != rawPid)
88 throw TexcHere(ToSBuf("Bad ", TheFile, " contains unreasonably large PID value: ", rawPid));
89
90 return finalPid;
91 }
92
93 /// determines whether a given process is running at the time of the call
94 static bool
95 ProcessIsRunning(const pid_t pid)
96 {
97 const auto result = kill(pid, 0);
98 const auto savedErrno = errno;
99 if (result != 0)
100 debugs(50, 3, "kill(" << pid << ", 0) failed: " << xstrerr(savedErrno));
101 // if we do not have permissions to signal the process, then it is running
102 return (result == 0 || savedErrno == EPERM);
103 }
104
105 /// quits if another Squid instance (that owns the given PID file) is running
106 static void
107 ThrowIfAlreadyRunningWith(File &pidFile)
108 {
109 bool running = false;
110 SBuf description;
111 try {
112 const auto pid = GetOtherPid(pidFile);
113 description = ToSBuf(TheFile, " with PID ", pid);
114 running = ProcessIsRunning(pid);
115 }
116 catch (const std::exception &ex) {
117 debugs(50, 5, "assuming no other Squid instance: " << ex.what());
118 return;
119 }
120
121 if (running)
122 throw TexcHere(ToSBuf("Squid is already running: Found fresh instance ", description));
123
124 debugs(50, 5, "assuming stale instance " << description);
125 }
126
127 pid_t
128 Instance::Other()
129 {
130 const auto filename = PidFilename();
131 if (filename.isEmpty())
132 throw TexcHere("no pid_filename configured");
133
134 File pidFile(filename, File::Be::ReadOnly().locked());
135 return GetOtherPid(pidFile);
136 }
137
138 void
139 Instance::ThrowIfAlreadyRunning()
140 {
141 const auto filename = PidFilename();
142 if (filename.isEmpty())
143 return; // the check is impossible
144
145 if (const auto filePtr = File::Optional(filename, File::Be::ReadOnly().locked())) {
146 const std::unique_ptr<File> pidFile(filePtr);
147 ThrowIfAlreadyRunningWith(*pidFile);
148 } else {
149 // It is best to assume then to check because checking without a lock
150 // might lead to false positives that lead to no Squid starting at all!
151 debugs(50, 5, "cannot lock " << TheFile << "; assuming no other Squid is running");
152 // If our assumption is false, we will fail to _create_ the PID file,
153 // and, hence, will not start, allowing that other Squid to run.
154 }
155 }
156
157 /// ties Instance::WriteOurPid() scheduler and RemoveInstance(void) handler
158 static SBuf ThePidFileToRemove;
159
160 /// atexit() handler; removes the PID file created with Instance::WriteOurPid()
161 static void
162 RemoveInstance()
163 {
164 if (ThePidFileToRemove.isEmpty()) // not the PidFilename()!
165 return; // nothing to do
166
167 debugs(50, Important(22), "Removing " << PidFileDescription(ThePidFileToRemove));
168
169 // Do not write to cache_log after our PID file is removed because another
170 // instance may already be logging there. Stop logging now because, if we
171 // wait until safeunlink(), some debugs() may slip through into the now
172 // "unlocked" cache_log, especially if we avoid the sensitive suid() area.
173 // Use stderr to capture late debugs() that did not make it into cache_log.
174 Debug::StopCacheLogUse();
175
176 const char *filename = ThePidFileToRemove.c_str(); // avoid complex operations inside enter_suid()
177 enter_suid();
178 safeunlink(filename, 0);
179 leave_suid();
180
181 ThePidFileToRemove.clear();
182 }
183
184 /// creates a PID file; throws on error
185 void
186 Instance::WriteOurPid()
187 {
188 // Instance code assumes that we do not support PID filename reconfiguration
189 static bool called = false;
190 Must(!called);
191 called = true;
192
193 const auto filename = PidFilename();
194 if (filename.isEmpty())
195 return; // nothing to do
196
197 File pidFile(filename, File::Be::ReadWrite().locked().createdIfMissing().openedByRoot());
198
199 // another instance may have started after the caller checked (if it did)
200 ThrowIfAlreadyRunningWith(pidFile);
201
202 /* now we know that we own the PID file created and/or locked above */
203
204 // Cleanup is scheduled through atexit() to ensure both:
205 // - cleanup upon fatal() and similar "unplanned" exits and
206 // - enter_suid() existence and proper logging support during cleanup.
207 // Even without PID filename reconfiguration support, we have to remember
208 // the file name we have used because Config.pidFilename may change!
209 (void)std::atexit(&RemoveInstance); // failures leave the PID file on disk
210 ThePidFileToRemove = filename;
211
212 /* write our PID to the locked file */
213 SBuf pidBuf;
214 pidBuf.Printf("%d\n", static_cast<int>(getpid()));
215 pidFile.truncate();
216 pidFile.writeAll(pidBuf);
217
218 // We must fsync before releasing the lock or other Squid processes may not see
219 // our written PID (and decide that they are dealing with a corrupted PID file).
220 pidFile.synchronize();
221
222 debugs(50, Important(23), "Created " << TheFile);
223 }
224