GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/loader_talk.cc
Date: 2026-09-27 02:40:09
Exec Total Coverage
Lines: 0 100 0.0%
Branches: 0 140 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5
6 #include "loader_talk.h"
7
8 #include <errno.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11 #include <unistd.h>
12
13 #include <cassert>
14 #include <cstdlib>
15
16 #include "loader.h"
17 #include "util/exception.h"
18 #include "util/logging.h"
19 #include "util/posix.h"
20
21 using namespace std; // NOLINT
22
23 namespace loader {
24 namespace loader_talk {
25
26 bool spawned_ = false;
27 string *socket_path_ = NULL;
28 int socket_fd_ = -1;
29 pthread_t thread_talk_;
30
31 ✗ bool Init(const string &socket_path) {
32 ✗ spawned_ = false;
33 ✗ socket_path_ = new string(socket_path);
34
35 ✗ socket_fd_ = MakeSocket(*socket_path_, 0600);
36 ✗ if (socket_fd_ == -1)
37 ✗ return false;
38 ✗ if (listen(socket_fd_, 1) == -1) {
39 LogCvmfs(kLogCvmfs, kLogDebug, "listening on socket failed (%d)", errno);
40 ✗ return false;
41 }
42
43 ✗ unlink((socket_path + ".paused.crashed").c_str());
44 ✗ unlink((socket_path + ".paused").c_str());
45
46 ✗ return true;
47 }
48
49
50 ✗ static void *MainTalk(void *data __attribute__((unused))) {
51 struct sockaddr_un remote;
52 ✗ socklen_t socket_size = sizeof(remote);
53 ✗ int con_fd = -1;
54 while (true) {
55 ✗ if (con_fd >= 0) {
56 ✗ shutdown(con_fd, SHUT_RDWR);
57 ✗ close(con_fd);
58 }
59 ✗ if ((con_fd = accept(socket_fd_, (struct sockaddr *)&remote, &socket_size))
60 ✗ < 0) {
61 ✗ break;
62 }
63
64 char command;
65 ✗ ReloadMode reload_mode = kReloadLegacy;
66 ✗ if (recv(con_fd, &command, 1, 0) > 0) {
67 ✗ if ((command == 'd') || (command == 'n')) {
68 // version that specifies reloading in debug or non-debug mode
69 // receives 2 commands
70 // first: debug (d) / non-debug(n)
71 // second: 'R' or 'S'
72 ✗ reload_mode = command == 'd' ? kReloadDebug : kReloadNoDebug;
73 ✗ if (recv(con_fd, &command, 1, 0) > 0) {
74 ✗ if ((command != 'R') && (command != 'S')) {
75 ✗ SendMsg2Socket(con_fd, "unknown command\n");
76 ✗ continue;
77 }
78 }
79 ✗ } else if ((command != 'R') && (command != 'S')) { // legacy support
80 ✗ SendMsg2Socket(con_fd, "unknown command\n");
81 ✗ continue;
82 }
83
84 ✗ SetLogMicroSyslog(*usyslog_path_);
85 ✗ LogCvmfs(kLogCvmfs, kLogSyslog, "reloading Fuse module. Reload mode=%d",
86 reload_mode);
87 ✗ int retval = Reload(con_fd, command == 'S', reload_mode);
88 ✗ SendMsg2Socket(con_fd, "~");
89 ✗ (void)send(con_fd, &retval, sizeof(retval), MSG_NOSIGNAL);
90 ✗ if (retval != kFailOk) {
91 ✗ PANIC(kLogSyslogErr, "reloading Fuse module failed (%d - %s)", retval,
92 Code2Ascii(static_cast<Failures>(retval)));
93 }
94 ✗ SetLogMicroSyslog("");
95 }
96 }
97
98 ✗ return NULL;
99 }
100
101
102 ✗ void Spawn() {
103 int retval;
104 ✗ retval = pthread_create(&thread_talk_, NULL, MainTalk, NULL);
105 ✗ assert(retval == 0);
106 ✗ spawned_ = true;
107 }
108
109
110 ✗ void Fini() {
111 ✗ unlink(socket_path_->c_str());
112 ✗ shutdown(socket_fd_, SHUT_RDWR);
113 ✗ close(socket_fd_);
114 ✗ if (spawned_)
115 ✗ pthread_join(thread_talk_, NULL);
116
117 ✗ delete socket_path_;
118 ✗ socket_path_ = NULL;
119 ✗ spawned_ = false;
120 ✗ socket_fd_ = -1;
121 }
122
123
124 /**
125 * Connects to a loader socket and triggers the reload
126 */
127 ✗ int MainReload(const std::string &socket_path, const bool stop_and_go,
128 const bool debug) {
129 ✗ LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak,
130 "Connecting to CernVM-FS loader... ");
131 ✗ int socket_fd = ConnectSocket(socket_path);
132 ✗ if (socket_fd < 0) {
133 ✗ LogCvmfs(kLogCvmfs, kLogStdout, "failed!");
134 ✗ return 100;
135 }
136 ✗ LogCvmfs(kLogCvmfs, kLogStdout, "done");
137
138 // reload mode: debug (d) or non-debug (n)
139 char commands[2];
140 ✗ commands[0] = debug ? 'd' : 'n';
141 ✗ commands[1] = stop_and_go ? 'S' : 'R';
142
143 // Loaders before version 2.11 won't recognize 'd' or 'n'. They will
144 // send "unknown command" and close the connection. We try to send
145 // both the commands and react according to what we read from the socket.
146
147 ssize_t retval;
148 do {
149 ✗ retval = send(socket_fd, commands, 2, MSG_NOSIGNAL);
150 ✗ } while ((retval <= 0) && (errno == EINTR));
151
152 ✗ if (retval <= 0) {
153 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "Sending reload command failed!");
154 ✗ return 103;
155 }
156
157 char buf;
158 ✗ std::string first_line;
159 ✗ bool past_first_line = false;
160 ✗ while ((retval = read(socket_fd, &buf, 1)) == 1) {
161 ✗ if (buf == '~')
162 ✗ break;
163
164 ✗ if (first_line == "unknown command") {
165 // We have a pre-2.11 loader, reconnect to the socket
166 ✗ LogCvmfs(kLogCvmfs, kLogStdout,
167 "Connecting in backwards compatibility mode");
168 ✗ close(socket_fd);
169 ✗ socket_fd = ConnectSocket(socket_path);
170 ✗ if (socket_fd < 0) {
171 ✗ LogCvmfs(kLogCvmfs, kLogStderr, "reconnecting failed!");
172 ✗ return 104;
173 }
174 ✗ WritePipe(socket_fd, &commands[1], 1);
175 ✗ first_line.clear();
176 ✗ past_first_line = true;
177 ✗ continue;
178 }
179
180 // chars are received one by one; in order to check if we get
181 // "unknown command" a string is constructed here
182 ✗ if (!past_first_line) {
183 ✗ if (buf == '\n') {
184 ✗ LogCvmfs(kLogCvmfs, kLogStdout, "%s", first_line.c_str());
185 ✗ past_first_line = true;
186 } else {
187 ✗ first_line.push_back(buf);
188 }
189 ✗ continue;
190 }
191
192 ✗ LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "%c", buf);
193 }
194 ✗ if (retval != 1) {
195 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
196 "Reload CRASHED! "
197 "CernVM-FS mountpoints unusable.");
198 ✗ return 101;
199 }
200
201 ✗ int result = 102;
202 ✗ if (read(socket_fd, &result, sizeof(result)) < 0) {
203 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
204 "Socket read FAILED! "
205 "CernVM-FS mountpoints unusable.");
206 } else {
207 ✗ if (result != kFailOk) {
208 ✗ LogCvmfs(kLogCvmfs, kLogStderr,
209 "Reload FAILED! "
210 "CernVM-FS mountpoints unusable.");
211 }
212 }
213
214 ✗ return result;
215 }
216
217 } // namespace loader_talk
218 } // namespace loader
219