]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include <sys/stat.h> | |
16 | #include <sys/utsname.h> | |
17 | #include <iostream> | |
18 | #include <string> | |
f67539c2 | 19 | #include <optional> |
7c673cae | 20 | |
f67539c2 | 21 | #include "common/async/context_pool.h" |
7c673cae FG |
22 | #include "common/config.h" |
23 | #include "common/errno.h" | |
24 | ||
25 | #include "client/Client.h" | |
26 | #include "client/fuse_ll.h" | |
27 | ||
28 | #include "msg/Messenger.h" | |
29 | ||
30 | #include "mon/MonClient.h" | |
31 | ||
32 | #include "common/Timer.h" | |
33 | #include "common/ceph_argparse.h" | |
34 | #if defined(__linux__) | |
35 | #include "common/linux_version.h" | |
36 | #endif | |
37 | #include "global/global_init.h" | |
38 | #include "global/signal_handler.h" | |
39 | #include "common/Preforker.h" | |
40 | #include "common/safe_io.h" | |
41 | ||
42 | #include <sys/types.h> | |
43 | #include <fcntl.h> | |
44 | ||
e306af50 | 45 | #include "include/ceph_fuse.h" |
1911f103 | 46 | #include <fuse_lowlevel.h> |
7c673cae FG |
47 | |
48 | #define dout_context g_ceph_context | |
49 | ||
20effc67 TL |
50 | using namespace std; |
51 | ||
f67539c2 TL |
52 | ceph::async::io_context_pool icp; |
53 | ||
7c673cae FG |
54 | static void fuse_usage() |
55 | { | |
56 | const char* argv[] = { | |
57 | "ceph-fuse", | |
58 | "-h", | |
59 | }; | |
60 | struct fuse_args args = FUSE_ARGS_INIT(2, (char**)argv); | |
1911f103 TL |
61 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
62 | struct fuse_cmdline_opts opts = {}; | |
f67539c2 TL |
63 | if (fuse_parse_cmdline(&args, &opts) != -1) { |
64 | if (opts.show_help) { | |
65 | cout << "usage: " << argv[0] << " [options] <mountpoint>\n\n"; | |
66 | cout << "FUSE options:\n"; | |
67 | fuse_cmdline_help(); | |
68 | fuse_lowlevel_help(); | |
69 | cout << "\n"; | |
70 | } | |
71 | } else { | |
1911f103 | 72 | #else |
11fdf7f2 | 73 | if (fuse_parse_cmdline(&args, nullptr, nullptr, nullptr) == -1) { |
1911f103 | 74 | #endif |
7c673cae FG |
75 | derr << "fuse_parse_cmdline failed." << dendl; |
76 | } | |
11fdf7f2 | 77 | ceph_assert(args.allocated); |
7c673cae FG |
78 | fuse_opt_free_args(&args); |
79 | } | |
80 | ||
81 | void usage() | |
82 | { | |
83 | cout << | |
c07f9fc5 FG |
84 | "usage: ceph-fuse [-n client.username] [-m mon-ip-addr:mon-port] <mount point> [OPTIONS]\n" |
85 | " --client_mountpoint/-r <sub_directory>\n" | |
86 | " use sub_directory as the mounted root, rather than the full Ceph tree.\n" | |
7c673cae FG |
87 | "\n"; |
88 | fuse_usage(); | |
89 | generic_client_usage(); | |
90 | } | |
91 | ||
92 | int main(int argc, const char **argv, const char *envp[]) { | |
93 | int filer_flags = 0; | |
94 | //cerr << "ceph-fuse starting " << myrank << "/" << world << std::endl; | |
20effc67 | 95 | auto args = argv_to_vec(argc, argv); |
7c673cae | 96 | if (args.empty()) { |
11fdf7f2 TL |
97 | cerr << argv[0] << ": -h or --help for usage" << std::endl; |
98 | exit(1); | |
99 | } | |
100 | if (ceph_argparse_need_usage(args)) { | |
7c673cae | 101 | usage(); |
11fdf7f2 | 102 | exit(0); |
7c673cae | 103 | } |
7c673cae | 104 | |
11fdf7f2 TL |
105 | std::map<std::string,std::string> defaults = { |
106 | { "pid_file", "" }, | |
107 | { "chdir", "/" } // FUSE will chdir("/"); be ready. | |
108 | }; | |
7c673cae | 109 | |
11fdf7f2 | 110 | auto cct = global_init(&defaults, args, CEPH_ENTITY_TYPE_CLIENT, |
7c673cae FG |
111 | CODE_ENVIRONMENT_DAEMON, |
112 | CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS); | |
11fdf7f2 TL |
113 | |
114 | for (auto i = args.begin(); i != args.end();) { | |
7c673cae FG |
115 | if (ceph_argparse_double_dash(args, i)) { |
116 | break; | |
11fdf7f2 | 117 | } else if (ceph_argparse_flag(args, i, "--localize-reads", (char*)nullptr)) { |
7c673cae FG |
118 | cerr << "setting CEPH_OSD_FLAG_LOCALIZE_READS" << std::endl; |
119 | filer_flags |= CEPH_OSD_FLAG_LOCALIZE_READS; | |
28e407b8 AA |
120 | } else if (ceph_argparse_flag(args, i, "-V", (char*)nullptr)) { |
121 | const char* tmpargv[] = { | |
122 | "ceph-fuse", | |
123 | "-V" | |
124 | }; | |
125 | ||
126 | struct fuse_args fargs = FUSE_ARGS_INIT(2, (char**)tmpargv); | |
1911f103 TL |
127 | #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) |
128 | struct fuse_cmdline_opts opts = {}; | |
129 | if (fuse_parse_cmdline(&fargs, &opts) == -1) { | |
130 | #else | |
28e407b8 | 131 | if (fuse_parse_cmdline(&fargs, nullptr, nullptr, nullptr) == -1) { |
1911f103 | 132 | #endif |
28e407b8 AA |
133 | derr << "fuse_parse_cmdline failed." << dendl; |
134 | } | |
11fdf7f2 | 135 | ceph_assert(fargs.allocated); |
28e407b8 AA |
136 | fuse_opt_free_args(&fargs); |
137 | exit(0); | |
7c673cae FG |
138 | } else { |
139 | ++i; | |
140 | } | |
141 | } | |
142 | ||
143 | // args for fuse | |
144 | const char **newargv; | |
145 | int newargc; | |
146 | vec_to_argv(argv[0], args, &newargc, &newargv); | |
147 | ||
7c673cae FG |
148 | // check for 32-bit arch |
149 | #ifndef __LP64__ | |
150 | cerr << std::endl; | |
151 | cerr << "WARNING: Ceph inode numbers are 64 bits wide, and FUSE on 32-bit kernels does" << std::endl; | |
152 | cerr << " not cope well with that situation. Expect to crash shortly." << std::endl; | |
153 | cerr << std::endl; | |
154 | #endif | |
155 | ||
156 | Preforker forker; | |
11fdf7f2 TL |
157 | auto daemonize = g_conf().get_val<bool>("daemonize"); |
158 | if (daemonize) { | |
7c673cae FG |
159 | global_init_prefork(g_ceph_context); |
160 | int r; | |
161 | string err; | |
162 | r = forker.prefork(err); | |
163 | if (r < 0 || forker.is_parent()) { | |
164 | // Start log if current process is about to exit. Otherwise, we hit an assert | |
165 | // in the Ceph context destructor. | |
166 | g_ceph_context->_log->start(); | |
167 | } | |
168 | if (r < 0) { | |
169 | cerr << "ceph-fuse " << err << std::endl; | |
170 | return r; | |
171 | } | |
172 | if (forker.is_parent()) { | |
173 | r = forker.parent_wait(err); | |
174 | if (r < 0) { | |
175 | cerr << "ceph-fuse " << err << std::endl; | |
176 | } | |
177 | return r; | |
178 | } | |
179 | global_init_postfork_start(cct.get()); | |
180 | } | |
181 | ||
182 | { | |
183 | common_init_finish(g_ceph_context); | |
adb31ebb | 184 | |
11fdf7f2 TL |
185 | init_async_signal_handler(); |
186 | register_async_signal_handler(SIGHUP, sighup_handler); | |
7c673cae FG |
187 | |
188 | //cout << "child, mounting" << std::endl; | |
189 | class RemountTest : public Thread { | |
190 | public: | |
191 | CephFuse *cfuse; | |
192 | Client *client; | |
11fdf7f2 | 193 | RemountTest() : cfuse(nullptr), client(nullptr) {} |
7c673cae FG |
194 | void init(CephFuse *cf, Client *cl) { |
195 | cfuse = cf; | |
196 | client = cl; | |
197 | } | |
198 | ~RemountTest() override {} | |
199 | void *entry() override { | |
200 | #if defined(__linux__) | |
39ae355f TL |
201 | bool can_invalidate_dentries = g_conf().get_val<bool>( |
202 | "client_try_dentry_invalidate"); | |
203 | uint64_t max_retries = g_conf().get_val<uint64_t>( | |
204 | "client_max_retries_on_remount_failure"); | |
205 | std::pair<int, bool> test_result; | |
206 | uint64_t i = 0; | |
207 | int tr = 0; | |
208 | do { | |
209 | test_result = client->test_dentry_handling(can_invalidate_dentries); | |
210 | tr = test_result.first; | |
211 | if (tr) { | |
212 | sleep(1); | |
213 | } | |
214 | } while (++i < max_retries && tr); | |
215 | ||
1d09f67e | 216 | bool abort_on_failure = test_result.second; |
11fdf7f2 | 217 | bool client_die_on_failed_dentry_invalidate = g_conf().get_val<bool>( |
b32b8144 FG |
218 | "client_die_on_failed_dentry_invalidate"); |
219 | if (tr != 0 && client_die_on_failed_dentry_invalidate) { | |
7c673cae FG |
220 | cerr << "ceph-fuse[" << getpid() |
221 | << "]: fuse failed dentry invalidate/remount test with error " | |
222 | << cpp_strerror(tr) << ", stopping" << std::endl; | |
223 | ||
224 | char buf[5050]; | |
225 | string mountpoint = cfuse->get_mount_point(); | |
226 | snprintf(buf, sizeof(buf), "fusermount -u -z %s", mountpoint.c_str()); | |
227 | int umount_r = system(buf); | |
228 | if (umount_r) { | |
229 | if (umount_r != -1) { | |
230 | if (WIFEXITED(umount_r)) { | |
231 | umount_r = WEXITSTATUS(umount_r); | |
232 | cerr << "got error " << umount_r | |
233 | << " when unmounting Ceph on failed remount test!" << std::endl; | |
234 | } else { | |
235 | cerr << "attempt to umount on failed remount test failed (on a signal?)" << std::endl; | |
236 | } | |
237 | } else { | |
238 | cerr << "system() invocation failed during remount test" << std::endl; | |
239 | } | |
240 | } | |
241 | } | |
1d09f67e TL |
242 | if(abort_on_failure) { |
243 | ceph_abort(); | |
244 | } | |
7c673cae FG |
245 | return reinterpret_cast<void*>(tr); |
246 | #else | |
247 | return reinterpret_cast<void*>(0); | |
248 | #endif | |
249 | } | |
250 | } tester; | |
251 | ||
252 | ||
253 | // get monmap | |
11fdf7f2 | 254 | Messenger *messenger = nullptr; |
7c673cae FG |
255 | StandaloneClient *client; |
256 | CephFuse *cfuse; | |
257 | UserPerm perms; | |
258 | int tester_r = 0; | |
11fdf7f2 | 259 | void *tester_rp = nullptr; |
7c673cae | 260 | |
f67539c2 TL |
261 | icp.start(cct->_conf.get_val<std::uint64_t>("client_asio_thread_count")); |
262 | MonClient *mc = new MonClient(g_ceph_context, icp); | |
7c673cae | 263 | int r = mc->build_initial_monmap(); |
11fdf7f2 TL |
264 | if (r == -EINVAL) { |
265 | cerr << "failed to generate initial mon list" << std::endl; | |
266 | exit(1); | |
267 | } | |
7c673cae FG |
268 | if (r < 0) |
269 | goto out_mc_start_failed; | |
270 | ||
271 | // start up network | |
272 | messenger = Messenger::create_client_messenger(g_ceph_context, "client"); | |
273 | messenger->set_default_policy(Messenger::Policy::lossy_client(0)); | |
274 | messenger->set_policy(entity_name_t::TYPE_MDS, | |
275 | Messenger::Policy::lossless_client(0)); | |
276 | ||
f67539c2 | 277 | client = new StandaloneClient(messenger, mc, icp); |
7c673cae FG |
278 | if (filer_flags) { |
279 | client->set_filer_flags(filer_flags); | |
280 | } | |
281 | ||
282 | cfuse = new CephFuse(client, forker.get_signal_fd()); | |
283 | ||
284 | r = cfuse->init(newargc, newargv); | |
285 | if (r != 0) { | |
286 | cerr << "ceph-fuse[" << getpid() << "]: fuse failed to initialize" << std::endl; | |
287 | goto out_messenger_start_failed; | |
288 | } | |
289 | ||
290 | cerr << "ceph-fuse[" << getpid() << "]: starting ceph client" << std::endl; | |
291 | r = messenger->start(); | |
292 | if (r < 0) { | |
293 | cerr << "ceph-fuse[" << getpid() << "]: ceph messenger failed with " << cpp_strerror(-r) << std::endl; | |
294 | goto out_messenger_start_failed; | |
295 | } | |
296 | ||
7c673cae FG |
297 | // start client |
298 | r = client->init(); | |
299 | if (r < 0) { | |
300 | cerr << "ceph-fuse[" << getpid() << "]: ceph client failed with " << cpp_strerror(-r) << std::endl; | |
301 | goto out_init_failed; | |
302 | } | |
303 | ||
304 | client->update_metadata("mount_point", cfuse->get_mount_point()); | |
305 | perms = client->pick_my_perms(); | |
11fdf7f2 TL |
306 | { |
307 | // start up fuse | |
308 | // use my argc, argv (make sure you pass a mount point!) | |
309 | auto client_mountpoint = g_conf().get_val<std::string>( | |
310 | "client_mountpoint"); | |
311 | auto mountpoint = client_mountpoint.c_str(); | |
312 | auto fuse_require_active_mds = g_conf().get_val<bool>( | |
313 | "fuse_require_active_mds"); | |
314 | r = client->mount(mountpoint, perms, fuse_require_active_mds); | |
315 | if (r < 0) { | |
316 | if (r == CEPH_FUSE_NO_MDS_UP) { | |
317 | cerr << "ceph-fuse[" << getpid() << "]: probably no MDS server is up?" << std::endl; | |
318 | } | |
319 | cerr << "ceph-fuse[" << getpid() << "]: ceph mount failed with " << cpp_strerror(-r) << std::endl; | |
320 | r = EXIT_FAILURE; | |
321 | goto out_shutdown; | |
28e407b8 | 322 | } |
7c673cae FG |
323 | } |
324 | ||
325 | r = cfuse->start(); | |
326 | if (r != 0) { | |
327 | cerr << "ceph-fuse[" << getpid() << "]: fuse failed to start" << std::endl; | |
328 | goto out_client_unmount; | |
329 | } | |
330 | ||
331 | cerr << "ceph-fuse[" << getpid() << "]: starting fuse" << std::endl; | |
332 | tester.init(cfuse, client); | |
333 | tester.create("tester"); | |
334 | r = cfuse->loop(); | |
335 | tester.join(&tester_rp); | |
336 | tester_r = static_cast<int>(reinterpret_cast<uint64_t>(tester_rp)); | |
337 | cerr << "ceph-fuse[" << getpid() << "]: fuse finished with error " << r | |
338 | << " and tester_r " << tester_r <<std::endl; | |
11fdf7f2 | 339 | |
7c673cae FG |
340 | out_client_unmount: |
341 | client->unmount(); | |
342 | cfuse->finalize(); | |
343 | out_shutdown: | |
f67539c2 | 344 | icp.stop(); |
7c673cae FG |
345 | client->shutdown(); |
346 | out_init_failed: | |
347 | unregister_async_signal_handler(SIGHUP, sighup_handler); | |
348 | shutdown_async_signal_handler(); | |
349 | ||
350 | // wait for messenger to finish | |
351 | messenger->shutdown(); | |
352 | messenger->wait(); | |
353 | out_messenger_start_failed: | |
354 | delete cfuse; | |
11fdf7f2 | 355 | cfuse = nullptr; |
7c673cae | 356 | delete client; |
11fdf7f2 | 357 | client = nullptr; |
7c673cae | 358 | delete messenger; |
11fdf7f2 | 359 | messenger = nullptr; |
7c673cae FG |
360 | out_mc_start_failed: |
361 | free(newargv); | |
362 | delete mc; | |
11fdf7f2 | 363 | mc = nullptr; |
7c673cae FG |
364 | //cout << "child done" << std::endl; |
365 | return forker.signal_exit(r); | |
366 | } | |
367 | } |