GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/pipe.h
Date: 2025-09-28 02:35:26
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 485 Pipe() {
66 int pipe_fd[2];
67
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
485 MakePipe(pipe_fd);
68 485 fd_read_ = pipe_fd[0];
69 485 fd_write_ = pipe_fd[1];
70 485 }
71
72 /**
73 * Destructor closes all valid file descriptors of the pipe
74 */
75 335 ~Pipe() { Close(); }
76
77 /**
78 * Closes all open file descriptors of the pipe and marks them as invalid
79 */
80 383 void Close() {
81 383 CloseReadFd();
82 383 CloseWriteFd();
83 383 }
84
85 /**
86 * Closes file descriptor that reads from the pipe and marks it as invalid.
87 */
88 845 void CloseReadFd() {
89
2/2
✓ Branch 0 taken 381 times.
✓ Branch 1 taken 265 times.
845 if (fd_read_ >= 0) {
90 529 close(fd_read_);
91 529 fd_read_ = -1;
92 }
93 845 }
94
95 /**
96 * Closes file descriptor that writes to the pipe and marks it as invalid.
97 */
98 943 void CloseWriteFd() {
99
2/2
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 265 times.
943 if (fd_write_ >= 0) {
100 627 close(fd_write_);
101 627 fd_write_ = -1;
102 }
103 943 }
104
105 /**
106 * Tries to write an object to the pipe
107 *
108 * @returns true if the entire object was written
109 * false otherwise
110 */
111 template<typename T>
112 bool TryWrite(const T &data) {
113 const int num_bytes = write(fd_write_, &data, sizeof(T));
114 return (num_bytes >= 0) && (static_cast<size_t>(num_bytes) == sizeof(T));
115 }
116
117 /**
118 * Writes an object to the pipe
119 *
120 * @returns true on success
121 * otherwise kills the program with an assert
122 *
123 */
124 template<typename T>
125 296 bool Write(const T &data) {
126 296 WritePipe(fd_write_, &data, sizeof(T));
127 296 return true;
128 }
129
130 /**
131 * Writes an object to the pipe
132 * If possible, it is recommended to use "bool Write(const T &data)"
133 *
134 * @returns true on success
135 * otherwise kills the program with an assert
136 *
137 */
138 52 bool Write(const void *buf, size_t nbyte) {
139 52 WritePipe(fd_write_, buf, nbyte);
140 52 return true;
141 }
142
143 /**
144 * (Re)tries to read from the pipe until it receives data or returned error
145 * is NOT a system interrupt
146 *
147 * @returns true if sizeof(data) bytes were received
148 * false otherwise
149 */
150 template<typename T>
151 138 bool TryRead(T *data) {
152 ssize_t num_bytes;
153 do {
154 138 num_bytes = read(fd_read_, data, sizeof(T));
155
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
138 } while ((num_bytes < 0) && (errno == EINTR));
156
3/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 48 times.
138 return (num_bytes >= 0) && (static_cast<size_t>(num_bytes) == sizeof(T));
157 }
158
159 /**
160 * Reads an object from the pipe
161 *
162 * @returns true on success
163 * otherwise kills the program with an assert
164 */
165 template<typename T>
166 762 bool Read(T *data) {
167 762 ReadPipe(fd_read_, data, sizeof(T));
168 762 return true;
169 }
170
171 /**
172 * Reads an object from the pipe
173 * If possible, it is recommend to use "bool Read(T *data)""
174 *
175 * @returns true on success
176 * otherwise kills the program with an assert
177 */
178 24 bool Read(void *buf, size_t nbyte) {
179 24 ReadPipe(fd_read_, buf, nbyte);
180 24 return true;
181 }
182
183 /**
184 * Returns the file descriptor that reads from the pipe
185 */
186 123 int GetReadFd() const { return fd_read_; }
187
188 /**
189 * Returns the file descriptor that writes to the pipe
190 */
191 134 int GetWriteFd() const { return fd_write_; }
192
193
194 private:
195 int fd_read_;
196 int fd_write_;
197
198 /**
199 * Only used in the unit tests to test pipes using stdin/stdout as read/write.
200 */
201 42 Pipe(const int fd_read, const int fd_write)
202 42 : fd_read_(fd_read), fd_write_(fd_write) { }
203
204 /**
205 * Creating a pipe should always succeed.
206 */
207 485 void MakePipe(int pipe_fd[2]) {
208 485 const int retval = pipe(pipe_fd);
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 338 times.
485 if (retval != 0) {
210 PANIC(kLogSyslogErr | kLogDebug, "MakePipe failed with errno %d", errno);
211 }
212 485 }
213
214
215 /**
216 * Writes to a pipe should always succeed.
217 */
218 250 void WritePipe(int fd, const void *buf, size_t nbyte) {
219 ssize_t num_bytes;
220 do {
221 250 num_bytes = write(fd, buf, nbyte);
222
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 200 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
250 } while ((num_bytes < 0) && (errno == EINTR));
223
2/4
✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 200 times.
250 if (!((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte))) {
224 PANIC(kLogSyslogErr | kLogDebug,
225 "WritePipe failed: expected write size %lu, "
226 "actually written %lu, errno %d, fd %d",
227 nbyte, num_bytes, errno, fd);
228 }
229 250 }
230
231
232 /**
233 * Reads from a pipe should always succeed.
234 */
235 454 void ReadPipe(int fd, void *buf, size_t nbyte) {
236 ssize_t num_bytes;
237 do {
238 454 num_bytes = read(fd, buf, nbyte);
239
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 405 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
454 } while ((num_bytes < 0) && (errno == EINTR));
240
2/4
✓ Branch 0 taken 405 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 405 times.
454 if (!((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte))) {
241 PANIC(kLogSyslogErr | kLogDebug,
242 "ReadPipe failed: expected read size %lu, "
243 "actually read %lu, errno %d, fd %d",
244 nbyte, num_bytes, errno, fd);
245 }
246 454 }
247 };
248
249 #ifdef CVMFS_NAMESPACE_GUARD
250 } // namespace CVMFS_NAMESPACE_GUARD
251 #endif
252
253 #endif // CVMFS_UTIL_PIPE_H_
254