GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/loader_talk.cc
Date: 2026-05-03 02:36:16
Exec Total Coverage
Lines: 0 100 0.0%
Branches: 0 136 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