]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_main.cc
bump version to 15.2.1-pve1
[ceph.git] / ceph / src / rgw / rgw_main.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3
4 #include "common/ceph_argparse.h"
5 #include "global/global_init.h"
6 #include "global/signal_handler.h"
7 #include "common/config.h"
8 #include "common/errno.h"
9 #include "common/Timer.h"
10 #include "common/safe_io.h"
11 #include "common/TracepointProvider.h"
12 #include "common/numa.h"
13 #include "include/compat.h"
14 #include "include/str_list.h"
15 #include "include/stringify.h"
16 #include "rgw_common.h"
17 #include "rgw_rados.h"
18 #include "rgw_period_pusher.h"
19 #include "rgw_realm_reloader.h"
20 #include "rgw_rest.h"
21 #include "rgw_rest_s3.h"
22 #include "rgw_rest_swift.h"
23 #include "rgw_rest_admin.h"
24 #include "rgw_rest_usage.h"
25 #include "rgw_rest_user.h"
26 #include "rgw_rest_bucket.h"
27 #include "rgw_rest_metadata.h"
28 #include "rgw_rest_log.h"
29 #include "rgw_rest_config.h"
30 #include "rgw_rest_realm.h"
31 #include "rgw_rest_sts.h"
32 #include "rgw_swift_auth.h"
33 #include "rgw_log.h"
34 #include "rgw_tools.h"
35 #include "rgw_resolve.h"
36 #include "rgw_request.h"
37 #include "rgw_process.h"
38 #include "rgw_frontend.h"
39 #include "rgw_http_client_curl.h"
40 #include "rgw_perf_counters.h"
41 #ifdef WITH_RADOSGW_AMQP_ENDPOINT
42 #include "rgw_amqp.h"
43 #endif
44 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
45 #include "rgw_kafka.h"
46 #endif
47 #if defined(WITH_RADOSGW_BEAST_FRONTEND)
48 #include "rgw_asio_frontend.h"
49 #endif /* WITH_RADOSGW_BEAST_FRONTEND */
50 #include "rgw_dmclock_scheduler_ctx.h"
51
52 #include "services/svc_zone.h"
53
54 #ifdef HAVE_SYS_PRCTL_H
55 #include <sys/prctl.h>
56 #endif
57
58 #define dout_subsys ceph_subsys_rgw
59
60 namespace {
61 TracepointProvider::Traits rgw_op_tracepoint_traits("librgw_op_tp.so",
62 "rgw_op_tracing");
63 TracepointProvider::Traits rgw_rados_tracepoint_traits("librgw_rados_tp.so",
64 "rgw_rados_tracing");
65 }
66
67 static sig_t sighandler_alrm;
68
69 class RGWProcess;
70
71 static int signal_fd[2] = {0, 0};
72
73 void signal_shutdown()
74 {
75 int val = 0;
76 int ret = write(signal_fd[0], (char *)&val, sizeof(val));
77 if (ret < 0) {
78 derr << "ERROR: " << __func__ << ": write() returned "
79 << cpp_strerror(errno) << dendl;
80 }
81 }
82
83 static void wait_shutdown()
84 {
85 int val;
86 int r = safe_read_exact(signal_fd[1], &val, sizeof(val));
87 if (r < 0) {
88 derr << "safe_read_exact returned with error" << dendl;
89 }
90 }
91
92 static int signal_fd_init()
93 {
94 return socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fd);
95 }
96
97 static void signal_fd_finalize()
98 {
99 close(signal_fd[0]);
100 close(signal_fd[1]);
101 }
102
103 static void handle_sigterm(int signum)
104 {
105 dout(1) << __func__ << dendl;
106 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
107 FCGX_ShutdownPending();
108 #endif
109
110 // send a signal to make fcgi's accept(2) wake up. unfortunately the
111 // initial signal often isn't sufficient because we race with accept's
112 // check of the flag wet by ShutdownPending() above.
113 if (signum != SIGUSR1) {
114 signal_shutdown();
115
116 // safety net in case we get stuck doing an orderly shutdown.
117 uint64_t secs = g_ceph_context->_conf->rgw_exit_timeout_secs;
118 if (secs)
119 alarm(secs);
120 dout(1) << __func__ << " set alarm for " << secs << dendl;
121 }
122
123 }
124
125 static void godown_alarm(int signum)
126 {
127 _exit(0);
128 }
129
130
131 class C_InitTimeout : public Context {
132 public:
133 C_InitTimeout() {}
134 void finish(int r) override {
135 derr << "Initialization timeout, failed to initialize" << dendl;
136 exit(1);
137 }
138 };
139
140 static int usage()
141 {
142 cout << "usage: radosgw [options...]" << std::endl;
143 cout << "options:\n";
144 cout << " --rgw-region=<region> region in which radosgw runs\n";
145 cout << " --rgw-zone=<zone> zone in which radosgw runs\n";
146 cout << " --rgw-socket-path=<path> specify a unix domain socket path\n";
147 cout << " -m monaddress[:port] connect to specified monitor\n";
148 cout << " --keyring=<path> path to radosgw keyring\n";
149 cout << " --logfile=<logfile> file to log debug output\n";
150 cout << " --debug-rgw=<log-level>/<memory-level> set radosgw debug level\n";
151 generic_server_usage();
152
153 return 0;
154 }
155
156 static RGWRESTMgr *set_logging(RGWRESTMgr *mgr)
157 {
158 mgr->set_logging(true);
159 return mgr;
160 }
161
162 static RGWRESTMgr *rest_filter(RGWRados *store, int dialect, RGWRESTMgr *orig)
163 {
164 RGWSyncModuleInstanceRef sync_module = store->get_sync_module();
165 if (sync_module) {
166 return sync_module->get_rest_filter(dialect, orig);
167 } else {
168 return orig;
169 }
170 }
171
172 /*
173 * start up the RADOS connection and then handle HTTP messages as they come in
174 */
175 int radosgw_Main(int argc, const char **argv)
176 {
177 // dout() messages will be sent to stderr, but FCGX wants messages on stdout
178 // Redirect stderr to stdout.
179 TEMP_FAILURE_RETRY(close(STDERR_FILENO));
180 if (TEMP_FAILURE_RETRY(dup2(STDOUT_FILENO, STDERR_FILENO)) < 0) {
181 int err = errno;
182 cout << "failed to redirect stderr to stdout: " << cpp_strerror(err)
183 << std::endl;
184 return ENOSYS;
185 }
186
187 /* alternative default for module */
188 map<string,string> defaults = {
189 { "debug_rgw", "1/5" },
190 { "keyring", "$rgw_data/keyring" },
191 { "objecter_inflight_ops", "24576" }
192 };
193
194 vector<const char*> args;
195 argv_to_vec(argc, argv, args);
196 if (args.empty()) {
197 cerr << argv[0] << ": -h or --help for usage" << std::endl;
198 exit(1);
199 }
200 if (ceph_argparse_need_usage(args)) {
201 usage();
202 exit(0);
203 }
204
205 int flags = CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS;
206 global_pre_init(
207 &defaults, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_DAEMON,
208 flags);
209
210 // Now that we've determined which frontend(s) to use, continue with global
211 // initialization. Passing false as the final argument ensures that
212 // global_pre_init() is not invoked twice.
213 // claim the reference and release it after subsequent destructors have fired
214 auto cct = global_init(&defaults, args, CEPH_ENTITY_TYPE_CLIENT,
215 CODE_ENVIRONMENT_DAEMON,
216 flags, "rgw_data", false);
217
218 // First, let's determine which frontends are configured.
219 list<string> frontends;
220 string rgw_frontends_str = g_conf().get_val<string>("rgw_frontends");
221 g_conf().early_expand_meta(rgw_frontends_str, &cerr);
222 get_str_list(rgw_frontends_str, ",", frontends);
223 multimap<string, RGWFrontendConfig *> fe_map;
224 list<RGWFrontendConfig *> configs;
225 if (frontends.empty()) {
226 frontends.push_back("civetweb");
227 }
228 for (list<string>::iterator iter = frontends.begin(); iter != frontends.end(); ++iter) {
229 string& f = *iter;
230
231 if (f.find("civetweb") != string::npos || f.find("beast") != string::npos) {
232 // If civetweb or beast is configured as a frontend, prevent global_init() from
233 // dropping permissions by setting the appropriate flag.
234 flags |= CINIT_FLAG_DEFER_DROP_PRIVILEGES;
235 if (f.find("port") != string::npos) {
236 // check for the most common ws problems
237 if ((f.find("port=") == string::npos) ||
238 (f.find("port= ") != string::npos)) {
239 derr << "WARNING: radosgw frontend config found unexpected spacing around 'port' "
240 << "(ensure frontend port parameter has the form 'port=80' with no spaces "
241 << "before or after '=')" << dendl;
242 }
243 }
244 }
245
246 RGWFrontendConfig *config = new RGWFrontendConfig(f);
247 int r = config->init();
248 if (r < 0) {
249 delete config;
250 cerr << "ERROR: failed to init config: " << f << std::endl;
251 return EINVAL;
252 }
253
254 configs.push_back(config);
255
256 string framework = config->get_framework();
257 fe_map.insert(pair<string, RGWFrontendConfig*>(framework, config));
258 }
259
260 int numa_node = g_conf().get_val<int64_t>("rgw_numa_node");
261 size_t numa_cpu_set_size = 0;
262 cpu_set_t numa_cpu_set;
263
264 if (numa_node >= 0) {
265 int r = get_numa_node_cpu_set(numa_node, &numa_cpu_set_size, &numa_cpu_set);
266 if (r < 0) {
267 dout(1) << __func__ << " unable to determine rgw numa node " << numa_node
268 << " CPUs" << dendl;
269 numa_node = -1;
270 } else {
271 r = set_cpu_affinity_all_threads(numa_cpu_set_size, &numa_cpu_set);
272 if (r < 0) {
273 derr << __func__ << " failed to set numa affinity: " << cpp_strerror(r)
274 << dendl;
275 }
276 }
277 } else {
278 dout(1) << __func__ << " not setting numa affinity" << dendl;
279 }
280
281 // maintain existing region root pool for new multisite objects
282 if (!g_conf()->rgw_region_root_pool.empty()) {
283 const char *root_pool = g_conf()->rgw_region_root_pool.c_str();
284 if (g_conf()->rgw_zonegroup_root_pool.empty()) {
285 g_conf().set_val_or_die("rgw_zonegroup_root_pool", root_pool);
286 }
287 if (g_conf()->rgw_period_root_pool.empty()) {
288 g_conf().set_val_or_die("rgw_period_root_pool", root_pool);
289 }
290 if (g_conf()->rgw_realm_root_pool.empty()) {
291 g_conf().set_val_or_die("rgw_realm_root_pool", root_pool);
292 }
293 }
294
295 // for region -> zonegroup conversion (must happen before common_init_finish())
296 if (!g_conf()->rgw_region.empty() && g_conf()->rgw_zonegroup.empty()) {
297 g_conf().set_val_or_die("rgw_zonegroup", g_conf()->rgw_region.c_str());
298 }
299
300 if (g_conf()->daemonize) {
301 global_init_daemonize(g_ceph_context);
302 }
303 ceph::mutex mutex = ceph::make_mutex("main");
304 SafeTimer init_timer(g_ceph_context, mutex);
305 init_timer.init();
306 mutex.lock();
307 init_timer.add_event_after(g_conf()->rgw_init_timeout, new C_InitTimeout);
308 mutex.unlock();
309
310 common_init_finish(g_ceph_context);
311
312 init_async_signal_handler();
313 register_async_signal_handler(SIGHUP, sighup_handler);
314
315 TracepointProvider::initialize<rgw_rados_tracepoint_traits>(g_ceph_context);
316 TracepointProvider::initialize<rgw_op_tracepoint_traits>(g_ceph_context);
317
318 int r = rgw_tools_init(g_ceph_context);
319 if (r < 0) {
320 derr << "ERROR: unable to initialize rgw tools" << dendl;
321 return -r;
322 }
323
324 rgw_init_resolver();
325 rgw::curl::setup_curl(fe_map);
326 rgw_http_client_init(g_ceph_context);
327
328 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
329 FCGX_Init();
330 #endif
331
332 rgw::sal::RGWRadosStore *store =
333 RGWStoreManager::get_storage(g_ceph_context,
334 g_conf()->rgw_enable_gc_threads,
335 g_conf()->rgw_enable_lc_threads,
336 g_conf()->rgw_enable_quota_threads,
337 g_conf()->rgw_run_sync_thread,
338 g_conf().get_val<bool>("rgw_dynamic_resharding"),
339 g_conf()->rgw_cache_enabled);
340 if (!store) {
341 mutex.lock();
342 init_timer.cancel_all_events();
343 init_timer.shutdown();
344 mutex.unlock();
345
346 derr << "Couldn't init storage provider (RADOS)" << dendl;
347 return EIO;
348 }
349 r = rgw_perf_start(g_ceph_context);
350 if (r < 0) {
351 derr << "ERROR: failed starting rgw perf" << dendl;
352 return -r;
353 }
354
355 rgw_rest_init(g_ceph_context, store->svc()->zone->get_zonegroup());
356
357 mutex.lock();
358 init_timer.cancel_all_events();
359 init_timer.shutdown();
360 mutex.unlock();
361
362 rgw_log_usage_init(g_ceph_context, store->getRados());
363
364 RGWREST rest;
365
366 list<string> apis;
367
368 get_str_list(g_conf()->rgw_enable_apis, apis);
369
370 map<string, bool> apis_map;
371 for (list<string>::iterator li = apis.begin(); li != apis.end(); ++li) {
372 apis_map[*li] = true;
373 }
374
375 /* warn about insecure keystone secret config options */
376 if (!(g_ceph_context->_conf->rgw_keystone_admin_token.empty() ||
377 g_ceph_context->_conf->rgw_keystone_admin_password.empty())) {
378 dout(0) << "WARNING: rgw_keystone_admin_token and rgw_keystone_admin_password should be avoided as they can expose secrets. Prefer the new rgw_keystone_admin_token_path and rgw_keystone_admin_password_path options, which read their secrets from files." << dendl;
379 }
380
381 // S3 website mode is a specialization of S3
382 const bool s3website_enabled = apis_map.count("s3website") > 0;
383 const bool sts_enabled = apis_map.count("sts") > 0;
384 const bool iam_enabled = apis_map.count("iam") > 0;
385 const bool pubsub_enabled = apis_map.count("pubsub") > 0;
386 // Swift API entrypoint could placed in the root instead of S3
387 const bool swift_at_root = g_conf()->rgw_swift_url_prefix == "/";
388 if (apis_map.count("s3") > 0 || s3website_enabled) {
389 if (! swift_at_root) {
390 rest.register_default_mgr(set_logging(rest_filter(store->getRados(), RGW_REST_S3,
391 new RGWRESTMgr_S3(s3website_enabled, sts_enabled, iam_enabled, pubsub_enabled))));
392 } else {
393 derr << "Cannot have the S3 or S3 Website enabled together with "
394 << "Swift API placed in the root of hierarchy" << dendl;
395 return EINVAL;
396 }
397 }
398
399 if (pubsub_enabled) {
400 #ifdef WITH_RADOSGW_AMQP_ENDPOINT
401 if (!rgw::amqp::init(cct.get())) {
402 dout(1) << "ERROR: failed to initialize AMQP manager" << dendl;
403 }
404 #endif
405 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
406 if (!rgw::kafka::init(cct.get())) {
407 dout(1) << "ERROR: failed to initialize Kafka manager" << dendl;
408 }
409 #endif
410 }
411
412 if (apis_map.count("swift") > 0) {
413 RGWRESTMgr_SWIFT* const swift_resource = new RGWRESTMgr_SWIFT;
414
415 if (! g_conf()->rgw_cross_domain_policy.empty()) {
416 swift_resource->register_resource("crossdomain.xml",
417 set_logging(new RGWRESTMgr_SWIFT_CrossDomain));
418 }
419
420 swift_resource->register_resource("healthcheck",
421 set_logging(new RGWRESTMgr_SWIFT_HealthCheck));
422
423 swift_resource->register_resource("info",
424 set_logging(new RGWRESTMgr_SWIFT_Info));
425
426 if (! swift_at_root) {
427 rest.register_resource(g_conf()->rgw_swift_url_prefix,
428 set_logging(rest_filter(store->getRados(), RGW_REST_SWIFT,
429 swift_resource)));
430 } else {
431 if (store->svc()->zone->get_zonegroup().zones.size() > 1) {
432 derr << "Placing Swift API in the root of URL hierarchy while running"
433 << " multi-site configuration requires another instance of RadosGW"
434 << " with S3 API enabled!" << dendl;
435 }
436
437 rest.register_default_mgr(set_logging(swift_resource));
438 }
439 }
440
441 if (apis_map.count("swift_auth") > 0) {
442 rest.register_resource(g_conf()->rgw_swift_auth_entry,
443 set_logging(new RGWRESTMgr_SWIFT_Auth));
444 }
445
446 if (apis_map.count("admin") > 0) {
447 RGWRESTMgr_Admin *admin_resource = new RGWRESTMgr_Admin;
448 admin_resource->register_resource("usage", new RGWRESTMgr_Usage);
449 admin_resource->register_resource("user", new RGWRESTMgr_User);
450 admin_resource->register_resource("bucket", new RGWRESTMgr_Bucket);
451
452 /*Registering resource for /admin/metadata */
453 admin_resource->register_resource("metadata", new RGWRESTMgr_Metadata);
454 admin_resource->register_resource("log", new RGWRESTMgr_Log);
455 admin_resource->register_resource("config", new RGWRESTMgr_Config);
456 admin_resource->register_resource("realm", new RGWRESTMgr_Realm);
457 rest.register_resource(g_conf()->rgw_admin_entry, admin_resource);
458 }
459
460 /* Initialize the registry of auth strategies which will coordinate
461 * the dynamic reconfiguration. */
462 rgw::auth::ImplicitTenants implicit_tenant_context{g_conf()};
463 g_conf().add_observer(&implicit_tenant_context);
464 auto auth_registry = \
465 rgw::auth::StrategyRegistry::create(g_ceph_context, implicit_tenant_context, store->getRados()->pctl);
466
467 /* Header custom behavior */
468 rest.register_x_headers(g_conf()->rgw_log_http_headers);
469
470 if (cct->_conf.get_val<std::string>("rgw_scheduler_type") == "dmclock" &&
471 !cct->check_experimental_feature_enabled("dmclock")){
472 derr << "dmclock scheduler type is experimental and needs to be"
473 << "set in the option enable experimental data corrupting features"
474 << dendl;
475 return EINVAL;
476 }
477
478 rgw::dmclock::SchedulerCtx sched_ctx{cct.get()};
479
480 OpsLogSocket *olog = NULL;
481
482 if (!g_conf()->rgw_ops_log_socket_path.empty()) {
483 olog = new OpsLogSocket(g_ceph_context, g_conf()->rgw_ops_log_data_backlog);
484 olog->init(g_conf()->rgw_ops_log_socket_path);
485 }
486
487 r = signal_fd_init();
488 if (r < 0) {
489 derr << "ERROR: unable to initialize signal fds" << dendl;
490 exit(1);
491 }
492
493 register_async_signal_handler(SIGTERM, handle_sigterm);
494 register_async_signal_handler(SIGINT, handle_sigterm);
495 register_async_signal_handler(SIGUSR1, handle_sigterm);
496 sighandler_alrm = signal(SIGALRM, godown_alarm);
497
498 map<string, string> service_map_meta;
499 service_map_meta["pid"] = stringify(getpid());
500
501 list<RGWFrontend *> fes;
502
503 string frontend_defs_str = g_conf().get_val<string>("rgw_frontend_defaults");
504
505 list<string> frontends_def;
506 get_str_list(frontend_defs_str, ",", frontends_def);
507
508 map<string, std::unique_ptr<RGWFrontendConfig> > fe_def_map;
509 for (auto& f : frontends_def) {
510 RGWFrontendConfig *config = new RGWFrontendConfig(f);
511 int r = config->init();
512 if (r < 0) {
513 delete config;
514 cerr << "ERROR: failed to init default config: " << f << std::endl;
515 return EINVAL;
516 }
517
518 fe_def_map[config->get_framework()].reset(config);
519 }
520
521 int fe_count = 0;
522
523 for (multimap<string, RGWFrontendConfig *>::iterator fiter = fe_map.begin();
524 fiter != fe_map.end(); ++fiter, ++fe_count) {
525 RGWFrontendConfig *config = fiter->second;
526 string framework = config->get_framework();
527
528 auto def_iter = fe_def_map.find(framework);
529 if (def_iter != fe_def_map.end()) {
530 config->set_default_config(*def_iter->second);
531 }
532
533 RGWFrontend *fe = NULL;
534
535 if (framework == "civetweb" || framework == "mongoose") {
536 framework = "civetweb";
537 std::string uri_prefix;
538 config->get_val("prefix", "", &uri_prefix);
539
540 RGWProcessEnv env = { store, &rest, olog, 0, uri_prefix, auth_registry };
541 //TODO: move all of scheduler initializations to frontends?
542
543 fe = new RGWCivetWebFrontend(env, config, sched_ctx);
544 }
545 else if (framework == "loadgen") {
546 int port;
547 config->get_val("port", 80, &port);
548 std::string uri_prefix;
549 config->get_val("prefix", "", &uri_prefix);
550
551 RGWProcessEnv env = { store, &rest, olog, port, uri_prefix, auth_registry };
552
553 fe = new RGWLoadGenFrontend(env, config);
554 }
555 #if defined(WITH_RADOSGW_BEAST_FRONTEND)
556 else if (framework == "beast") {
557 int port;
558 config->get_val("port", 80, &port);
559 std::string uri_prefix;
560 config->get_val("prefix", "", &uri_prefix);
561 RGWProcessEnv env{ store, &rest, olog, port, uri_prefix, auth_registry };
562 fe = new RGWAsioFrontend(env, config, sched_ctx);
563 }
564 #endif /* WITH_RADOSGW_BEAST_FRONTEND */
565 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
566 else if (framework == "fastcgi" || framework == "fcgi") {
567 framework = "fastcgi";
568 std::string uri_prefix;
569 config->get_val("prefix", "", &uri_prefix);
570 RGWProcessEnv fcgi_pe = { store, &rest, olog, 0, uri_prefix, auth_registry };
571
572 fe = new RGWFCGXFrontend(fcgi_pe, config);
573 }
574 #endif /* WITH_RADOSGW_FCGI_FRONTEND */
575
576 service_map_meta["frontend_type#" + stringify(fe_count)] = framework;
577 service_map_meta["frontend_config#" + stringify(fe_count)] = config->get_config();
578
579 if (fe == NULL) {
580 dout(0) << "WARNING: skipping unknown framework: " << framework << dendl;
581 continue;
582 }
583
584 dout(0) << "starting handler: " << fiter->first << dendl;
585 int r = fe->init();
586 if (r < 0) {
587 derr << "ERROR: failed initializing frontend" << dendl;
588 return -r;
589 }
590 r = fe->run();
591 if (r < 0) {
592 derr << "ERROR: failed run" << dendl;
593 return -r;
594 }
595
596 fes.push_back(fe);
597 }
598
599 r = store->getRados()->register_to_service_map("rgw", service_map_meta);
600 if (r < 0) {
601 derr << "ERROR: failed to register to service map: " << cpp_strerror(-r) << dendl;
602
603 /* ignore error */
604 }
605
606
607 // add a watcher to respond to realm configuration changes
608 RGWPeriodPusher pusher(store);
609 RGWFrontendPauser pauser(fes, implicit_tenant_context, &pusher);
610 RGWRealmReloader reloader(store, service_map_meta, &pauser);
611
612 RGWRealmWatcher realm_watcher(g_ceph_context, store->svc()->zone->get_realm());
613 realm_watcher.add_watcher(RGWRealmNotify::Reload, reloader);
614 realm_watcher.add_watcher(RGWRealmNotify::ZonesNeedPeriod, pusher);
615
616 #if defined(HAVE_SYS_PRCTL_H)
617 if (prctl(PR_SET_DUMPABLE, 1) == -1) {
618 cerr << "warning: unable to set dumpable flag: " << cpp_strerror(errno) << std::endl;
619 }
620 #endif
621
622 wait_shutdown();
623
624 derr << "shutting down" << dendl;
625
626 for (list<RGWFrontend *>::iterator liter = fes.begin(); liter != fes.end();
627 ++liter) {
628 RGWFrontend *fe = *liter;
629 fe->stop();
630 }
631
632 for (list<RGWFrontend *>::iterator liter = fes.begin(); liter != fes.end();
633 ++liter) {
634 RGWFrontend *fe = *liter;
635 fe->join();
636 delete fe;
637 }
638
639 for (list<RGWFrontendConfig *>::iterator liter = configs.begin();
640 liter != configs.end(); ++liter) {
641 RGWFrontendConfig *fec = *liter;
642 delete fec;
643 }
644
645 unregister_async_signal_handler(SIGHUP, sighup_handler);
646 unregister_async_signal_handler(SIGTERM, handle_sigterm);
647 unregister_async_signal_handler(SIGINT, handle_sigterm);
648 unregister_async_signal_handler(SIGUSR1, handle_sigterm);
649 shutdown_async_signal_handler();
650
651 rgw_log_usage_finalize();
652
653 delete olog;
654
655 RGWStoreManager::close_storage(store);
656 rgw::auth::s3::LDAPEngine::shutdown();
657 rgw_tools_cleanup();
658 rgw_shutdown_resolver();
659 rgw_http_client_cleanup();
660 rgw::curl::cleanup_curl();
661 g_conf().remove_observer(&implicit_tenant_context);
662 #ifdef WITH_RADOSGW_AMQP_ENDPOINT
663 rgw::amqp::shutdown();
664 #endif
665 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
666 rgw::kafka::shutdown();
667 #endif
668
669 rgw_perf_stop(g_ceph_context);
670
671 dout(1) << "final shutdown" << dendl;
672
673 signal_fd_finalize();
674
675 return 0;
676 }
677
678 extern "C" {
679
680 int radosgw_main(int argc, const char** argv)
681 {
682 return radosgw_Main(argc, argv);
683 }
684
685 } /* extern "C" */
686