@@ -56,7 +56,7 @@ bool has_sshfs(const std::string& name, mp::SSHSession& session)
56
56
// Check if snap support is installed in the instance
57
57
if (session.exec (" which snap" ).exit_code () != 0 )
58
58
{
59
- mpl::log (mpl::Level::warning, category, fmt::format ( " Snap support is not installed in '{}'" , name) );
59
+ mpl::warn ( category, " Snap support is not installed in '{}'" , name);
60
60
throw std::runtime_error (
61
61
fmt::format (" Snap support needs to be installed in '{}' in order to support mounts.\n "
62
62
" Please see https://docs.snapcraft.io/installing-snapd for information on\n "
@@ -70,15 +70,14 @@ bool has_sshfs(const std::string& name, mp::SSHSession& session)
70
70
// Check if multipass-sshfs is already installed
71
71
if (session.exec (" sudo snap list multipass-sshfs" ).exit_code (std::chrono::seconds (15 )) == 0 )
72
72
{
73
- mpl::log (mpl::Level::debug, category,
74
- fmt::format (" The multipass-sshfs snap is already installed on '{}'" , name));
73
+ mpl::debug (category, " The multipass-sshfs snap is already installed on '{}'" , name);
75
74
return true ;
76
75
}
77
76
78
77
// Check if /snap exists for "classic" snap support
79
78
if (session.exec (" [ -e /snap ]" ).exit_code () != 0 )
80
79
{
81
- mpl::log (mpl::Level::warning, category, fmt::format ( " Classic snap support symlink is needed in '{}'" , name) );
80
+ mpl::warn ( category, " Classic snap support symlink is needed in '{}'" , name);
82
81
throw std::runtime_error (
83
82
fmt::format (" Classic snap support is not enabled for '{}'!\n\n "
84
83
" Please see https://docs.snapcraft.io/installing-snapd for information on\n "
@@ -92,22 +91,18 @@ bool has_sshfs(const std::string& name, mp::SSHSession& session)
92
91
void install_sshfs_for (const std::string& name, mp::SSHSession& session, const std::chrono::milliseconds& timeout)
93
92
try
94
93
{
95
- mpl::log (mpl::Level:: info, category, fmt::format ( " Installing the multipass-sshfs snap in '{}'" , name) );
94
+ mpl::info ( category, " Installing the multipass-sshfs snap in '{}'" , name);
96
95
auto proc = session.exec (" sudo snap install multipass-sshfs" );
97
96
if (proc.exit_code (timeout) != 0 )
98
97
{
99
98
auto error_msg = proc.read_std_error ();
100
- mpl::log (mpl::Level::error,
101
- category,
102
- fmt::format (" Failed to install 'multipass-sshfs': {}" , mpu::trim_end (error_msg)));
99
+ mpl::error (category, " Failed to install 'multipass-sshfs': {}" , mpu::trim_end (error_msg));
103
100
throw mp::SSHFSMissingError ();
104
101
}
105
102
}
106
103
catch (const mp::ExitlessSSHProcessException& e)
107
104
{
108
- mpl::log (mpl::Level::error,
109
- category,
110
- fmt::format (" Could not install 'multipass-sshfs' in '{}': {}" , name, e.what ()));
105
+ mpl::error (category, " Could not install 'multipass-sshfs' in '{}': {}" , name, e.what ());
111
106
throw mp::SSHFSMissingError ();
112
107
}
113
108
} // namespace
@@ -130,30 +125,7 @@ SSHFSMountHandler::SSHFSMountHandler(VirtualMachine* vm,
130
125
this ->mount_spec .get_gid_mappings (),
131
126
this ->mount_spec .get_uid_mappings ()}
132
127
{
133
- mpl::log (
134
- mpl::Level::info,
135
- category,
136
- fmt::format (" initializing mount {} => {} in '{}'" , this ->mount_spec .get_source_path (), target, vm->vm_name ));
137
- }
138
-
139
- bool SSHFSMountHandler::is_active ()
140
- try
141
- {
142
- if (!active || !process || !process->running ())
143
- return false ;
144
-
145
- SSHSession session{vm->ssh_hostname (), vm->ssh_port (), vm->ssh_username (), *ssh_key_provider};
146
-
147
- const auto resolved_target = mp::utils::get_resolved_target (session, target);
148
-
149
- return !session.exec (fmt::format (" findmnt --type fuse.sshfs | grep -E '^{} +:{}'" , resolved_target, source))
150
- .exit_code ();
151
- }
152
- catch (const std::exception& e)
153
- {
154
- mpl::log (mpl::Level::warning, category,
155
- fmt::format (" Failed checking SSHFS mount \" {}\" in instance '{}': {}" , target, vm->vm_name , e.what ()));
156
- return false ;
128
+ mpl::info (category, " initializing mount {} => {} in '{}'" , this ->mount_spec .get_source_path (), target, vm->vm_name );
157
129
}
158
130
159
131
void SSHFSMountHandler::activate_impl (ServerVariant server, std::chrono::milliseconds timeout)
@@ -177,32 +149,34 @@ void SSHFSMountHandler::activate_impl(ServerVariant server, std::chrono::millise
177
149
config.host = vm->ssh_hostname ();
178
150
config.port = vm->ssh_port ();
179
151
180
- if (process)
181
- process.reset ();
182
152
process.reset (platform::make_sshfs_server_process (config).release ());
183
153
184
154
QObject::connect (process.get (), &Process::finished, [this ](const ProcessState& exit_state) {
185
155
if (exit_state.completed_successfully ())
186
156
{
187
- mpl::log (mpl::Level::info, category,
188
- fmt::format (" Mount \" {}\" in instance '{}' has stopped" , target, vm->vm_name ));
157
+ mpl::info (category, " Mount \" {}\" in instance '{}' has stopped" , target, vm->vm_name );
189
158
}
190
159
else
191
160
{
192
161
// not error as it failing can indicate we need to install sshfs in the VM
193
- mpl::log (mpl::Level::warning, category,
194
- fmt::format (" Mount \" {}\" in instance '{}' has stopped unsuccessfully: {}" , target, vm->vm_name ,
195
- exit_state.failure_message ()));
162
+ mpl::warn (category,
163
+ " Mount \" {}\" in instance '{}' has stopped unsuccessfully: {}" ,
164
+ target,
165
+ vm->vm_name ,
166
+ exit_state.failure_message ());
196
167
}
197
168
});
198
169
QObject::connect (process.get (), &Process::error_occurred, [this ](auto error, auto error_string) {
199
- mpl::log (mpl::Level::error, category,
200
- fmt::format (" There was an error with sshfs_server for instance '{}' with path \" {}\" : {} - {}" ,
201
- vm->vm_name , target, mpu::qenum_to_string (error), error_string));
170
+ mpl::error (category,
171
+ " There was an error with sshfs_server for instance '{}' with path \" {}\" : {} - {}" ,
172
+ vm->vm_name ,
173
+ target,
174
+ mpu::qenum_to_string (error),
175
+ error_string);
202
176
});
203
177
204
- mpl::log (mpl::Level:: info, category, fmt::format ( " process program '{}'" , process->program () ));
205
- mpl::log (mpl::Level:: info, category, fmt::format ( " process arguments '{}'" , process->arguments ().join (" , " ) ));
178
+ mpl::info ( category, " process program '{}'" , process->program ());
179
+ mpl::info ( category, " process arguments '{}'" , process->arguments ().join (" , " ));
206
180
207
181
start_and_block_until_connected (process.get ());
208
182
// after the process is started, it must be moved to the main thread
@@ -211,9 +185,10 @@ void SSHFSMountHandler::activate_impl(ServerVariant server, std::chrono::millise
211
185
// when stopping the mount from the main thread again, qt will try to send an event from the main thread to the one
212
186
// in which the process lives this will result in an error since qt can't send events from one thread to another
213
187
process->moveToThread (QCoreApplication::instance ()->thread ());
188
+ // So, for any future travelers, this^ is the main reason why we use qt_delete_later_unique_ptr thingy.
214
189
215
190
// Check in case sshfs_server stopped, usually due to an error
216
- auto process_state = process->process_state ();
191
+ const auto process_state = process->process_state ();
217
192
if (process_state.exit_code == 9 ) // Magic number returned by sshfs_server
218
193
throw SSHFSMissingError ();
219
194
else if (process_state.exit_code || process_state.error )
@@ -223,18 +198,45 @@ void SSHFSMountHandler::activate_impl(ServerVariant server, std::chrono::millise
223
198
224
199
void SSHFSMountHandler::deactivate_impl (bool force)
225
200
{
226
- mpl::log (mpl::Level:: info, category, fmt::format (" Stopping mount \" {}\" in instance '{}'" , target, vm->vm_name ));
201
+ mpl::info ( category, fmt::format (" Stopping mount \" {}\" in instance '{}'" , target, vm->vm_name ));
227
202
QObject::disconnect (process.get (), &Process::error_occurred, nullptr , nullptr );
228
- if (process->terminate (); !process->wait_for_finished (5000 ))
203
+
204
+ constexpr auto kProcessWaitTimeout = std::chrono::milliseconds{5000 };
205
+ if (process->terminate (); !process->wait_for_finished (kProcessWaitTimeout .count ()))
229
206
{
230
- auto err = fmt::format (" Failed to terminate SSHFS mount process: {}" , process->read_all_standard_error ());
207
+ auto fetch_stderr = [](Process& process) {
208
+ return fmt::format (" Failed to terminate SSHFS mount process gracefully: {}" ,
209
+ process.read_all_standard_error ());
210
+ };
211
+
212
+ const auto err = fetch_stderr (*process.get ());
213
+
231
214
if (force)
232
- mpl::log (
233
- mpl::Level::warning, category,
234
- fmt::format (" Failed to gracefully stop mount \" {}\" in instance '{}': {}" , target, vm->vm_name , err));
215
+ {
216
+ mpl::warn (category,
217
+ " Failed to gracefully stop mount \" {}\" in instance '{}': {}, trying to stop it forcefully." ,
218
+ target,
219
+ vm->vm_name ,
220
+ err);
221
+ /* *
222
+ * Let's try brute force this time.
223
+ */
224
+ process->kill ();
225
+ const auto result = process->wait_for_finished (kProcessWaitTimeout .count ());
226
+
227
+ mpl::warn (category,
228
+ " {} to forcefully stop mount \" {}\" in instance '{}': {}" ,
229
+ result ? " Succeeded" : " Failed" ,
230
+ target,
231
+ vm->vm_name ,
232
+ result ? " " : fetch_stderr (*process.get ()));
233
+ }
235
234
else
236
235
throw std::runtime_error{err};
237
236
}
237
+
238
+ // Finally, call reset() to disconnect all the signals and defer the deletion
239
+ // to the owning thread, in this case it's the QT main.
238
240
process.reset ();
239
241
}
240
242
0 commit comments