GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/util/pipe.h
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 58 61 95.1%
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
13 #include "exception.h"
14 #include "gtest/gtest_prod.h"
15 #include "util/export.h"
16 #include "util/single_copy.h"
17
18 #ifdef CVMFS_NAMESPACE_GUARD
19 namespace CVMFS_NAMESPACE_GUARD {
20 #endif
21
22 /**
23 * Describes the functionality of a pipe used as a template parameter to
24 * the Pipe class. This makes it clear in stack traces which pipe is blocking.
25 */
26 enum PipeType {
27 kPipeThreadTerminator = 0, // pipe only used to signal a thread to stop
28 kPipeWatchdog,
29 kPipeWatchdogSupervisor,
30 kPipeWatchdogPid,
31 kPipeDetachedChild,
32 kPipeTest,
33 kPipeDownloadJobs,
34 kPipeDownloadJobsResults
35 };
36
37 /**
38 * Common signals used by pipes
39 */
40 enum PipeSignals {
41 kPipeTerminateSignal = 1
42 };
43
44 template <PipeType pipeType>
45 class CVMFS_EXPORT Pipe : public SingleCopy {
46 FRIEND_TEST(T_Util, ManagedExecRunShell);
47 FRIEND_TEST(T_Util, ManagedExecExecuteBinaryDoubleFork);
48 FRIEND_TEST(T_Util, ManagedExecExecuteBinaryAsChild);
49
50 public:
51 /**
52 * A pipe is a simple asynchronous communication mechanism. It establishes
53 * a unidirectional communication link between two file descriptors. One of
54 * them is used to only write to the pipe, the other one only to read from it.
55 *
56 * This class is a simple wrapper around the handling of a "standard" pipe
57 * that uses system calls.
58 *
59 * @note PipeType as class template parameter should symbolize the
60 * functionality of the specific type, independent of what variable
61 * name it has
62 */
63 41 Pipe() {
64 int pipe_fd[2];
65
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
41 MakePipe(pipe_fd);
66 41 fd_read_ = pipe_fd[0];
67 41 fd_write_ = pipe_fd[1];
68 41 }
69
70 /**
71 * Destructor closes all valid file descriptors of the pipe
72 */
73 44 ~Pipe() {
74 44 Close();
75 44 }
76
77 /**
78 * Closes all open file descriptors of the pipe and marks them as invalid
79 */
80 51 void Close() {
81 51 CloseReadFd();
82 51 CloseWriteFd();
83 51 }
84
85 /**
86 * Closes file descriptor that reads from the pipe and marks it as invalid.
87 */
88 183 void CloseReadFd() {
89
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 40 times.
183 if (fd_read_ >= 0) {
90 140 close(fd_read_);
91 140 fd_read_ = -1;
92 }
93 183 }
94
95 /**
96 * Closes file descriptor that writes to the pipe and marks it as invalid.
97 */
98 279 void CloseWriteFd() {
99
2/2
✓ Branch 0 taken 137 times.
✓ Branch 1 taken 40 times.
279 if (fd_write_ >= 0) {
100 236 close(fd_write_);
101 236 fd_write_ = -1;
102 }
103 279 }
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 234 bool Write(const T &data) {
126 234 WritePipe(fd_write_, &data, sizeof(T));
127 234 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 5 bool Write(const void *buf, size_t nbyte) {
139 5 WritePipe(fd_write_, buf, nbyte);
140 5 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 104 bool TryRead(T *data) {
152 ssize_t num_bytes;
153 do {
154 104 num_bytes = read(fd_read_, data, sizeof(T));
155
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
104 } while ((num_bytes < 0) && (errno == EINTR));
156
3/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 47 times.
104 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 122 bool Read(T *data) {
167 122 ReadPipe(fd_read_, data, sizeof(T));
168 122 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 2 bool Read(void *buf, size_t nbyte) {
179 2 ReadPipe(fd_read_, buf, nbyte);
180 2 return true;
181 }
182
183 /**
184 * Returns the file descriptor that reads from the pipe
185 */
186 101 int GetReadFd() const {
187 101 return fd_read_;
188 }
189
190 /**
191 * Returns the file descriptor that writes to the pipe
192 */
193 115 int GetWriteFd() const {
194 115 return fd_write_;
195 }
196
197
198 private:
199 int fd_read_;
200 int fd_write_;
201
202 /**
203 * Only used in the unit tests to test pipes using stdin/stdout as read/write.
204 */
205 6 Pipe(const int fd_read, const int fd_write) : fd_read_(fd_read),
206 3 fd_write_(fd_write) {}
207
208 /**
209 * Creating a pipe should always succeed.
210 */
211 41 void MakePipe(int pipe_fd[2]) {
212 41 int retval = pipe(pipe_fd);
213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
41 if (retval != 0) {
214 PANIC(kLogSyslogErr | kLogDebug, "MakePipe failed with errno %d", errno);
215 }
216 41 }
217
218
219 /**
220 * Writes to a pipe should always succeed.
221 */
222 171 void WritePipe(int fd, const void *buf, size_t nbyte) {
223 ssize_t num_bytes;
224 do {
225 171 num_bytes = write(fd, buf, nbyte);
226
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 122 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
171 } while ((num_bytes < 0) && (errno == EINTR));
227
2/4
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 122 times.
171 if (!((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte))) {
228 PANIC(kLogSyslogErr | kLogDebug,
229 "WritePipe failed: expected write size %lu, "
230 "actually written %lu, errno %d, fd %d",
231 nbyte, num_bytes, errno, fd);
232 }
233 171 }
234
235
236 /**
237 * Reads from a pipe should always succeed.
238 */
239 64 void ReadPipe(int fd, void *buf, size_t nbyte) {
240 ssize_t num_bytes;
241 do {
242 64 num_bytes = read(fd, buf, nbyte);
243
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
64 } while ((num_bytes < 0) && (errno == EINTR));
244
2/4
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 63 times.
64 if (!((num_bytes >= 0) && (static_cast<size_t>(num_bytes) == nbyte))) {
245 PANIC(kLogSyslogErr | kLogDebug,
246 "ReadPipe failed: expected read size %lu, "
247 "actually read %lu, errno %d, fd %d",
248 nbyte, num_bytes, errno, fd);
249 }
250 64 }
251 };
252
253 #ifdef CVMFS_NAMESPACE_GUARD
254 } // namespace CVMFS_NAMESPACE_GUARD
255 #endif
256
257 #endif // CVMFS_UTIL_PIPE_H_
258