GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/pipe.h
Date: 2026-03-15 02:35:27
Exec Total Coverage
Lines: 54 57 94.7%
Branches: 16 32 50.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #ifndef CVMFS_UTIL_PIPE_H_
6 #define CVMFS_UTIL_PIPE_H_
7
8 #include <sys/types.h>
9 #include <unistd.h>
10
11 #include <cerrno>
12 #include <cstddef>
13
14 #include "exception.h"
15 #include "duplex_testing.h"
16 #include "util/export.h"
17 #include "util/logging.h"
18 #include "util/single_copy.h"
19
20 #ifdef CVMFS_NAMESPACE_GUARD
21 namespace CVMFS_NAMESPACE_GUARD {
22 #endif
23
24 /**
25 * Describes the functionality of a pipe used as a template parameter to
26 * the Pipe class. This makes it clear in stack traces which pipe is blocking.
27 */
28 enum PipeType {
29 kPipeThreadTerminator = 0, // pipe only used to signal a thread to stop
30 kPipeWatchdog,
31 kPipeWatchdogSupervisor,
32 kPipeWatchdogPid,
33 kPipeDetachedChild,
34 kPipeTest,
35 kPipeDownloadJobs,
36 kPipeDownloadJobsResults
37 };
38
39 /**
40 * Common signals used by pipes
41 */
42 enum PipeSignals {
43 kPipeTerminateSignal = 1
44 };
45
46 template<PipeType pipeType>
47 class CVMFS_EXPORT Pipe : public SingleCopy {
48 FRIEND_TEST(T_Util, ManagedExecRunShell);
49 FRIEND_TEST(T_Util, ManagedExecExecuteBinaryDoubleFork);
50 FRIEND_TEST(T_Util, ManagedExecExecuteBinaryAsChild);
51
52 public:
53 /**
54 * A pipe is a simple asynchronous communication mechanism. It establishes
55 * a unidirectional communication link between two file descriptors. One of
56 * them is used to only write to the pipe, the other one only to read from it.
57 *
58 * This class is a simple wrapper around the handling of a "standard" pipe
59 * that uses system calls.
60 *
61 * @note PipeType as class template parameter should symbolize the
62 * functionality of the specific type, independent of what variable
63 * name it has
64 */
65 1597 Pipe() {
66 int pipe_fd[2];
67
1/2
✓ Branch 1 taken 1450 times.
✗ Branch 2 not taken.
1597 MakePipe(pipe_fd);
68 1597 fd_read_ = pipe_fd[0];
69 1597 fd_write_ = pipe_fd[1];
70 1597 }
71
72 /**
73 * Used in unit tests to test pipes using stdin/stdout as read/write
74 * and also when restoring state after reload.
75 */
76 74 Pipe(const int fd_read, const int fd_write)
77 74 : fd_read_(fd_read), fd_write_(fd_write) { }
78
79 /**
80 * Destructor closes all valid file descriptors of the pipe
81 */
82 1479 ~Pipe() { Close(); }
83
84 /**
85 * Closes all open file descriptors of the pipe and marks them as invalid
86 */
87 1739 void Close() {
88 1739 CloseReadFd();
89 1739 CloseWriteFd();
90 1739 }
91
92 /**
93 * Closes file descriptor that reads from the pipe and marks it as invalid.
94 */
95 3118 void CloseReadFd() {
96
2/2
✓ Branch 0 taken 1525 times.
✓ Branch 1 taken 1394 times.
3118 if (fd_read_ >= 0) {
97 1673 close(fd_read_);
98 1673 fd_read_ = -1;
99 }
100 3118 }
101
102 /**
103 * Closes file descriptor that writes to the pipe and marks it as invalid.
104 */
105 3216 void CloseWriteFd() {
106
2/2
✓ Branch 0 taken 1574 times.
✓ Branch 1 taken 1394 times.
3216 if (fd_write_ >= 0) {
107 1771 close(fd_write_);
108 1771 fd_write_ = -1;
109 }
110 3216 }
111
112 /**
113 * Tries to write an object to the pipe
114 *
115 * @returns true if the entire object was written
116 * false otherwise
117 */
118 template<typename T>
119 bool TryWrite(const T &data) {
120 const int num_bytes = write(fd_write_, &data, sizeof(T));
121 return (num_bytes >= 0) && (static_cast<size_t>(num_bytes) == sizeof(T));
122 }
123
124 /**
125 * Writes an object to the pipe
126 *
127 * @returns true on success
128 * otherwise kills the program with an assert
129 *
130 */
131 template<typename T>
132 920 bool Write(const T &data) {
133 920 WritePipe(fd_write_, &data, sizeof(T));
134 920 return true;
135 }
136
137 /**
138 * Writes an object to the pipe
139 * If possible, it is recommended to use "bool Write(const T &data)"
140 *
141 * @returns true on success
142 * otherwise kills the program with an assert
143 *
144 */
145 162 bool Write(const void *buf, size_t nbyte) {
146 162 WritePipe(fd_write_, buf, nbyte);
147 162 return true;
148 }
149
150 /**
151 * (Re)tries to read from the pipe until it receives data or returned error
152 * is NOT a system interrupt
153 *
154 * @returns true if sizeof(data) bytes were received
155 * false otherwise
156 */
157 template<typename T>
158 450 bool TryRead(T *data) {
159 ssize_t num_bytes;
160 do {
161 450 num_bytes = read(fd_read_, data, sizeof(T));
162
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
450 } while ((num_bytes < 0) && (errno == EINTR));
163
3/4
✓ Branch 0 taken 225 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 177 times.
✓ Branch 3 taken 48 times.
450 return (num_bytes >= 0) && (static_cast<size_t>(num_bytes) == sizeof(T));
164 }
165
166 /**
167 * Reads an object from the pipe
168 *
169 * @returns true on success
170 * otherwise kills the program with an assert
171 */
172 template<typename T>
173 4430 bool Read(T *data) {
174 4430 ReadPipe(fd_read_, data, sizeof(T));
175 4430 return true;
176 }
177
178 /**
179 * Reads an object from the pipe
180 * If possible, it is recommend to use "bool Read(T *data)""
181 *
182 * @returns true on success
183 * otherwise kills the program with an assert
184 */
185 78 bool Read(void *buf, size_t nbyte) {
186 78 ReadPipe(fd_read_, buf, nbyte);
187 78 return true;
188 }
189
190 /**
191 * Returns the file descriptor that reads from the pipe
192 */
193 318 int GetReadFd() const { return fd_read_; }
194
195 /**
196 * Returns the file descriptor that writes to the pipe
197 */
198 378 int GetWriteFd() const { return fd_write_; }
199
200
201 private:
202 int fd_read_;
203 int fd_write_;
204
205 /**
206 * Creating a pipe should always succeed.
207 */
208 1597 void MakePipe(int pipe_fd[2]) {
209 1597 const int retval = pipe(pipe_fd);
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1450 times.
1597 if (retval != 0) {
211 PANIC(kLogSyslogErr | kLogDebug, "MakePipe failed with errno %d", errno);
212 }
213 1597 }
214
215
216 /**
217 * Writes to a pipe should always succeed.
218 */
219 672 void WritePipe(int fd, const void *buf, size_t nbyte) {
220 ssize_t num_bytes;
221 do {
222 672 num_bytes = write(fd, buf, nbyte);
223
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 622 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
672 } while ((num_bytes < 0) && (errno == EINTR));
224
2/4
✓ Branch 0 taken 622 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 622 times.
672 if (!((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte))) {
225 PANIC(kLogSyslogErr | kLogDebug,
226 "WritePipe failed: expected write size %lu, "
227 "actually written %lu, errno %d, fd %d",
228 nbyte, num_bytes, errno, fd);
229 }
230 672 }
231
232
233 /**
234 * Reads from a pipe should always succeed.
235 */
236 2342 void ReadPipe(int fd, void *buf, size_t nbyte) {
237 ssize_t num_bytes;
238 do {
239 2342 num_bytes = read(fd, buf, nbyte);
240
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2293 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2342 } while ((num_bytes < 0) && (errno == EINTR));
241
2/4
✓ Branch 0 taken 2293 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2293 times.
2342 if (!((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte))) {
242 PANIC(kLogSyslogErr | kLogDebug,
243 "ReadPipe failed: expected read size %lu, "
244 "actually read %lu, errno %d, fd %d",
245 nbyte, num_bytes, errno, fd);
246 }
247 2342 }
248 };
249
250 #ifdef CVMFS_NAMESPACE_GUARD
251 } // namespace CVMFS_NAMESPACE_GUARD
252 #endif
253
254 #endif // CVMFS_UTIL_PIPE_H_
255