GCC Code Coverage Report


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