1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
5 * Ceph - scalable distributed file system
7 * Copyright (C) 2022 Red Hat, Inc
9 * This is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License version 2.1, as published by the Free Software
12 * Foundation. See file COPYING.
16 #include <boost/intrusive/list.hpp>
17 #include "global/global_init.h"
18 #include "global/signal_handler.h"
19 #include "common/config.h"
20 #include "common/errno.h"
21 #include "common/Timer.h"
22 #include "common/TracepointProvider.h"
23 #include "common/openssl_opts_handler.h"
24 #include "common/numa.h"
25 #include "include/compat.h"
26 #include "include/str_list.h"
27 #include "include/stringify.h"
29 #include "rgw_common.h"
30 #include "rgw_sal_rados.h"
31 #include "rgw_period_pusher.h"
32 #include "rgw_realm_reloader.h"
34 #include "rgw_rest_s3.h"
35 #include "rgw_rest_swift.h"
36 #include "rgw_rest_admin.h"
37 #include "rgw_rest_info.h"
38 #include "rgw_rest_usage.h"
39 #include "rgw_rest_bucket.h"
40 #include "rgw_rest_metadata.h"
41 #include "rgw_rest_log.h"
42 #include "rgw_rest_config.h"
43 #include "rgw_rest_realm.h"
44 #include "rgw_rest_ratelimit.h"
45 #include "rgw_swift_auth.h"
48 #include "rgw_frontend.h"
49 #include "rgw_lib_frontend.h"
50 #include "rgw_tools.h"
51 #include "rgw_resolve.h"
52 #include "rgw_process.h"
53 #include "rgw_frontend.h"
54 #include "rgw_http_client_curl.h"
55 #include "rgw_kmip_client.h"
56 #include "rgw_kmip_client_impl.h"
57 #include "rgw_perf_counters.h"
58 #include "rgw_signal.h"
59 #ifdef WITH_RADOSGW_AMQP_ENDPOINT
62 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
63 #include "rgw_kafka.h"
65 #ifdef WITH_ARROW_FLIGHT
66 #include "rgw_flight_frontend.h"
68 #include "rgw_asio_frontend.h"
69 #include "rgw_dmclock_scheduler_ctx.h"
71 #ifdef WITH_RADOSGW_DBSTORE
72 #include "rgw_sal_dbstore.h"
74 #include "rgw_lua_background.h"
75 #include "services/svc_zone.h"
77 #ifdef HAVE_SYS_PRCTL_H
78 #include <sys/prctl.h>
81 #define dout_subsys ceph_subsys_rgw
86 TracepointProvider::Traits
rgw_op_tracepoint_traits(
87 "librgw_op_tp.so", "rgw_op_tracing");
88 TracepointProvider::Traits
rgw_rados_tracepoint_traits(
89 "librgw_rados_tp.so", "rgw_rados_tracing");
92 OpsLogFile
* rgw::AppMain::ops_log_file
;
94 void rgw::AppMain::init_frontends1(bool nfs
)
97 std::string fe_key
= (nfs
) ? "rgw_nfs_frontends" : "rgw_frontends";
98 std::vector
<std::string
> frontends
;
99 std::string rgw_frontends_str
= g_conf().get_val
<string
>(fe_key
);
100 g_conf().early_expand_meta(rgw_frontends_str
, &cerr
);
101 get_str_vec(rgw_frontends_str
, ",", frontends
);
103 /* default frontends */
105 const auto is_rgw_nfs
= [](const auto& s
){return s
== "rgw-nfs";};
106 if (std::find_if(frontends
.begin(), frontends
.end(), is_rgw_nfs
) == frontends
.end()) {
107 frontends
.push_back("rgw-nfs");
110 if (frontends
.empty()) {
111 frontends
.push_back("beast");
115 for (auto &f
: frontends
) {
116 if (f
.find("beast") != string::npos
) {
117 have_http_frontend
= true;
118 if (f
.find("port") != string::npos
) {
119 // check for the most common ws problems
120 if ((f
.find("port=") == string::npos
) ||
121 (f
.find("port= ") != string::npos
)) {
123 R
"(WARNING: radosgw frontend config found unexpected spacing around 'port'
124 (ensure frontend port parameter has the form 'port=80' with no spaces
125 before or after '='))"
130 if (f
.find("civetweb") != string::npos
) {
131 have_http_frontend
= true;
135 RGWFrontendConfig
*config
= new RGWFrontendConfig(f
);
136 int r
= config
->init();
139 cerr
<< "ERROR: failed to init config: " << f
<< std::endl
;
143 fe_configs
.push_back(config
);
145 pair
<string
, RGWFrontendConfig
*>(config
->get_framework(), config
));
146 } /* for each frontend */
148 // maintain existing region root pool for new multisite objects
149 if (!g_conf()->rgw_region_root_pool
.empty()) {
150 const char *root_pool
= g_conf()->rgw_region_root_pool
.c_str();
151 if (g_conf()->rgw_zonegroup_root_pool
.empty()) {
152 g_conf().set_val_or_die("rgw_zonegroup_root_pool", root_pool
);
154 if (g_conf()->rgw_period_root_pool
.empty()) {
155 g_conf().set_val_or_die("rgw_period_root_pool", root_pool
);
157 if (g_conf()->rgw_realm_root_pool
.empty()) {
158 g_conf().set_val_or_die("rgw_realm_root_pool", root_pool
);
162 // for region -> zonegroup conversion (must happen before
163 // common_init_finish())
164 if (!g_conf()->rgw_region
.empty() && g_conf()->rgw_zonegroup
.empty()) {
165 g_conf().set_val_or_die("rgw_zonegroup", g_conf()->rgw_region
.c_str());
168 ceph::crypto::init_openssl_engine_once();
169 } /* init_frontends1 */
171 void rgw::AppMain::init_numa()
177 int numa_node
= g_conf().get_val
<int64_t>("rgw_numa_node");
178 size_t numa_cpu_set_size
= 0;
179 cpu_set_t numa_cpu_set
;
181 if (numa_node
>= 0) {
182 int r
= get_numa_node_cpu_set(numa_node
, &numa_cpu_set_size
, &numa_cpu_set
);
184 dout(1) << __func__
<< " unable to determine rgw numa node " << numa_node
188 r
= set_cpu_affinity_all_threads(numa_cpu_set_size
, &numa_cpu_set
);
190 derr
<< __func__
<< " failed to set numa affinity: " << cpp_strerror(r
)
195 dout(1) << __func__
<< " not setting numa affinity" << dendl
;
199 void rgw::AppMain::init_storage()
202 (g_conf()->rgw_enable_gc_threads
&&
203 ((!nfs
) || (nfs
&& g_conf()->rgw_nfs_run_gc_threads
)));
206 (g_conf()->rgw_enable_lc_threads
&&
207 ((!nfs
) || (nfs
&& g_conf()->rgw_nfs_run_lc_threads
)));
210 (g_conf()->rgw_enable_quota_threads
&&
211 ((!nfs
) || (nfs
&& g_conf()->rgw_nfs_run_quota_threads
)));
214 (g_conf()->rgw_run_sync_thread
&&
215 ((!nfs
) || (nfs
&& g_conf()->rgw_nfs_run_sync_thread
)));
217 DriverManager::Config cfg
= DriverManager::get_config(false, g_ceph_context
);
218 env
.driver
= DriverManager::get_storage(dpp
, dpp
->get_cct(),
224 g_conf().get_val
<bool>("rgw_dynamic_resharding"),
225 g_conf()->rgw_cache_enabled
);
229 void rgw::AppMain::init_perfcounters()
231 (void) rgw_perf_start(dpp
->get_cct());
232 } /* init_perfcounters */
234 void rgw::AppMain::init_http_clients()
237 rgw::curl::setup_curl(fe_map
);
238 rgw_http_client_init(dpp
->get_cct());
239 rgw_kmip_client_init(*new RGWKMIPManagerImpl(dpp
->get_cct()));
240 } /* init_http_clients */
242 void rgw::AppMain::cond_init_apis()
244 rgw_rest_init(g_ceph_context
, env
.driver
->get_zone()->get_zonegroup());
246 if (have_http_frontend
) {
247 std::vector
<std::string
> apis
;
248 get_str_vec(g_conf()->rgw_enable_apis
, apis
);
250 std::map
<std::string
, bool> apis_map
;
251 for (auto &api
: apis
) {
252 apis_map
[api
] = true;
255 /* warn about insecure keystone secret config options */
256 if (!(g_ceph_context
->_conf
->rgw_keystone_admin_token
.empty() ||
257 g_ceph_context
->_conf
->rgw_keystone_admin_password
.empty())) {
259 << "WARNING: rgw_keystone_admin_token and "
260 "rgw_keystone_admin_password should be avoided as they can "
261 "expose secrets. Prefer the new rgw_keystone_admin_token_path "
262 "and rgw_keystone_admin_password_path options, which read their "
263 "secrets from files."
267 // S3 website mode is a specialization of S3
268 const bool s3website_enabled
= apis_map
.count("s3website") > 0;
269 const bool sts_enabled
= apis_map
.count("sts") > 0;
270 const bool iam_enabled
= apis_map
.count("iam") > 0;
271 const bool pubsub_enabled
=
272 apis_map
.count("pubsub") > 0 || apis_map
.count("notifications") > 0;
273 // Swift API entrypoint could placed in the root instead of S3
274 const bool swift_at_root
= g_conf()->rgw_swift_url_prefix
== "/";
275 if (apis_map
.count("s3") > 0 || s3website_enabled
) {
276 if (!swift_at_root
) {
277 rest
.register_default_mgr(set_logging(
278 rest_filter(env
.driver
, RGW_REST_S3
,
279 new RGWRESTMgr_S3(s3website_enabled
, sts_enabled
,
280 iam_enabled
, pubsub_enabled
))));
282 derr
<< "Cannot have the S3 or S3 Website enabled together with "
283 << "Swift API placed in the root of hierarchy" << dendl
;
287 if (apis_map
.count("swift") > 0) {
288 RGWRESTMgr_SWIFT
* const swift_resource
= new RGWRESTMgr_SWIFT
;
290 if (! g_conf()->rgw_cross_domain_policy
.empty()) {
291 swift_resource
->register_resource("crossdomain.xml",
292 set_logging(new RGWRESTMgr_SWIFT_CrossDomain
));
295 swift_resource
->register_resource("healthcheck",
296 set_logging(new RGWRESTMgr_SWIFT_HealthCheck
));
298 swift_resource
->register_resource("info",
299 set_logging(new RGWRESTMgr_SWIFT_Info
));
301 if (! swift_at_root
) {
302 rest
.register_resource(g_conf()->rgw_swift_url_prefix
,
303 set_logging(rest_filter(env
.driver
, RGW_REST_SWIFT
,
306 if (env
.driver
->get_zone()->get_zonegroup().get_zone_count() > 1) {
307 derr
<< "Placing Swift API in the root of URL hierarchy while running"
308 << " multi-site configuration requires another instance of RadosGW"
309 << " with S3 API enabled!" << dendl
;
312 rest
.register_default_mgr(set_logging(swift_resource
));
316 if (apis_map
.count("swift_auth") > 0) {
317 rest
.register_resource(g_conf()->rgw_swift_auth_entry
,
318 set_logging(new RGWRESTMgr_SWIFT_Auth
));
321 if (apis_map
.count("admin") > 0) {
322 RGWRESTMgr_Admin
*admin_resource
= new RGWRESTMgr_Admin
;
323 admin_resource
->register_resource("info", new RGWRESTMgr_Info
);
324 admin_resource
->register_resource("usage", new RGWRESTMgr_Usage
);
325 /* Register driver-specific admin APIs */
326 env
.driver
->register_admin_apis(admin_resource
);
327 rest
.register_resource(g_conf()->rgw_admin_entry
, admin_resource
);
329 } /* have_http_frontend */
332 void rgw::AppMain::init_ldap()
334 CephContext
* cct
= env
.driver
->ctx();
335 const string
&ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
336 const string
&ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
337 const string
&ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
338 const string
&ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
339 const string
&ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
340 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
342 ldh
.reset(new rgw::LDAPHelper(ldap_uri
, ldap_binddn
,
343 ldap_bindpw
.c_str(), ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
));
348 void rgw::AppMain::init_opslog()
350 rgw_log_usage_init(dpp
->get_cct(), env
.driver
);
352 OpsLogManifold
*olog_manifold
= new OpsLogManifold();
353 if (!g_conf()->rgw_ops_log_socket_path
.empty()) {
354 OpsLogSocket
*olog_socket
=
355 new OpsLogSocket(g_ceph_context
, g_conf()->rgw_ops_log_data_backlog
);
356 olog_socket
->init(g_conf()->rgw_ops_log_socket_path
);
357 olog_manifold
->add_sink(olog_socket
);
359 if (!g_conf()->rgw_ops_log_file_path
.empty()) {
361 new OpsLogFile(g_ceph_context
, g_conf()->rgw_ops_log_file_path
,
362 g_conf()->rgw_ops_log_data_backlog
);
363 ops_log_file
->start();
364 olog_manifold
->add_sink(ops_log_file
);
366 olog_manifold
->add_sink(new OpsLogRados(env
.driver
));
367 olog
= olog_manifold
;
370 int rgw::AppMain::init_frontends2(RGWLib
* rgwlib
)
373 vector
<string
> frontends_def
;
374 std::string frontend_defs_str
=
375 g_conf().get_val
<string
>("rgw_frontend_defaults");
376 get_str_vec(frontend_defs_str
, ",", frontends_def
);
378 service_map_meta
["pid"] = stringify(getpid());
380 std::map
<std::string
, std::unique_ptr
<RGWFrontendConfig
> > fe_def_map
;
381 for (auto& f
: frontends_def
) {
382 RGWFrontendConfig
*config
= new RGWFrontendConfig(f
);
383 int r
= config
->init();
386 cerr
<< "ERROR: failed to init default config: " << f
<< std::endl
;
389 fe_def_map
[config
->get_framework()].reset(config
);
392 /* Initialize the registry of auth strategies which will coordinate
393 * the dynamic reconfiguration. */
394 implicit_tenant_context
.reset(new rgw::auth::ImplicitTenants
{g_conf()});
395 g_conf().add_observer(implicit_tenant_context
.get());
397 /* allocate a mime table (you'd never guess that from the name) */
398 rgw_tools_init(dpp
, dpp
->get_cct());
400 /* Header custom behavior */
401 rest
.register_x_headers(g_conf()->rgw_log_http_headers
);
403 sched_ctx
.reset(new rgw::dmclock::SchedulerCtx
{dpp
->get_cct()});
404 ratelimiter
.reset(new ActiveRateLimiter
{dpp
->get_cct()});
405 ratelimiter
->start();
407 // initialize RGWProcessEnv
410 env
.auth_registry
= rgw::auth::StrategyRegistry::create(
411 dpp
->get_cct(), *implicit_tenant_context
, env
.driver
);
412 env
.ratelimiting
= ratelimiter
.get();
415 for (multimap
<string
, RGWFrontendConfig
*>::iterator fiter
= fe_map
.begin();
416 fiter
!= fe_map
.end(); ++fiter
, ++fe_count
) {
417 RGWFrontendConfig
*config
= fiter
->second
;
418 string framework
= config
->get_framework();
420 auto def_iter
= fe_def_map
.find(framework
);
421 if (def_iter
!= fe_def_map
.end()) {
422 config
->set_default_config(*def_iter
->second
);
425 RGWFrontend
* fe
= nullptr;
427 if (framework
== "loadgen") {
428 fe
= new RGWLoadGenFrontend(env
, config
);
430 else if (framework
== "beast") {
431 fe
= new RGWAsioFrontend(env
, config
, *sched_ctx
);
433 else if (framework
== "rgw-nfs") {
434 fe
= new RGWLibFrontend(env
, config
);
436 rgwlib
->set_fe(static_cast<RGWLibFrontend
*>(fe
));
439 else if (framework
== "arrow_flight") {
440 #ifdef WITH_ARROW_FLIGHT
442 config
->get_val("port", 8077, &port
);
443 fe
= new rgw::flight::FlightFrontend(env
, config
, port
);
445 derr
<< "WARNING: arrow_flight frontend requested, but not included in build; skipping" << dendl
;
450 service_map_meta
["frontend_type#" + stringify(fe_count
)] = framework
;
451 service_map_meta
["frontend_config#" + stringify(fe_count
)] = config
->get_config();
454 dout(0) << "WARNING: skipping unknown framework: " << framework
<< dendl
;
458 dout(0) << "starting handler: " << fiter
->first
<< dendl
;
461 derr
<< "ERROR: failed initializing frontend" << dendl
;
466 derr
<< "ERROR: failed run" << dendl
;
473 std::string daemon_type
= (nfs
) ? "rgw-nfs" : "rgw";
474 r
= env
.driver
->register_to_service_map(dpp
, daemon_type
, service_map_meta
);
476 derr
<< "ERROR: failed to register to service map: " << cpp_strerror(-r
) << dendl
;
480 if (env
.driver
->get_name() == "rados") {
481 // add a watcher to respond to realm configuration changes
482 pusher
= std::make_unique
<RGWPeriodPusher
>(dpp
, env
.driver
, null_yield
);
483 fe_pauser
= std::make_unique
<RGWFrontendPauser
>(fes
, pusher
.get());
484 rgw_pauser
= std::make_unique
<RGWPauser
>();
485 rgw_pauser
->add_pauser(fe_pauser
.get());
486 if (env
.lua
.background
) {
487 rgw_pauser
->add_pauser(env
.lua
.background
);
489 reloader
= std::make_unique
<RGWRealmReloader
>(
490 env
, *implicit_tenant_context
, service_map_meta
, rgw_pauser
.get());
491 realm_watcher
= std::make_unique
<RGWRealmWatcher
>(dpp
, g_ceph_context
,
492 static_cast<rgw::sal::RadosStore
*>(env
.driver
)->svc()->zone
->get_realm());
493 realm_watcher
->add_watcher(RGWRealmNotify::Reload
, *reloader
);
494 realm_watcher
->add_watcher(RGWRealmNotify::ZonesNeedPeriod
, *pusher
.get());
498 } /* init_frontends2 */
500 void rgw::AppMain::init_tracepoints()
502 TracepointProvider::initialize
<rgw_rados_tracepoint_traits
>(dpp
->get_cct());
503 TracepointProvider::initialize
<rgw_op_tracepoint_traits
>(dpp
->get_cct());
504 tracing::rgw::tracer
.init("rgw");
505 } /* init_tracepoints() */
507 void rgw::AppMain::init_notification_endpoints()
509 #ifdef WITH_RADOSGW_AMQP_ENDPOINT
510 if (!rgw::amqp::init(dpp
->get_cct())) {
511 derr
<< "ERROR: failed to initialize AMQP manager" << dendl
;
514 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
515 if (!rgw::kafka::init(dpp
->get_cct())) {
516 derr
<< "ERROR: failed to initialize Kafka manager" << dendl
;
519 } /* init_notification_endpoints */
521 void rgw::AppMain::init_lua()
523 rgw::sal::Driver
* driver
= env
.driver
;
525 std::string path
= g_conf().get_val
<std::string
>("rgw_luarocks_location");
527 path
+= "/" + g_conf()->name
.to_str();
529 env
.lua
.luarocks_path
= path
;
531 #ifdef WITH_RADOSGW_LUA_PACKAGES
532 rgw::lua::packages_t failed_packages
;
534 r
= rgw::lua::install_packages(dpp
, driver
, null_yield
, path
,
535 failed_packages
, output
);
537 dout(1) << "WARNING: failed to install lua packages from allowlist"
540 if (!output
.empty()) {
541 dout(10) << "INFO: lua packages installation output: \n" << output
<< dendl
;
543 for (const auto &p
: failed_packages
) {
544 dout(5) << "WARNING: failed to install lua package: " << p
545 << " from allowlist" << dendl
;
549 env
.lua
.manager
= env
.driver
->get_lua_manager();
551 if (driver
->get_name() == "rados") { /* Supported for only RadosStore */
552 lua_background
= std::make_unique
<
553 rgw::lua::Background
>(driver
, dpp
->get_cct(), path
);
554 lua_background
->start();
555 env
.lua
.background
= lua_background
.get();
559 void rgw::AppMain::shutdown(std::function
<void(void)> finalize_async_signals
)
561 if (env
.driver
->get_name() == "rados") {
562 reloader
.reset(); // stop the realm reloader
565 for (auto& fe
: fes
) {
569 for (auto& fe
: fes
) {
574 for (auto& fec
: fe_configs
) {
578 ldh
.reset(nullptr); // deletes
579 finalize_async_signals(); // callback
580 rgw_log_usage_finalize();
584 if (lua_background
) {
585 lua_background
->shutdown();
588 DriverManager::close_storage(env
.driver
);
591 rgw_shutdown_resolver();
592 rgw_http_client_cleanup();
593 rgw_kmip_client_cleanup();
594 rgw::curl::cleanup_curl();
595 g_conf().remove_observer(implicit_tenant_context
.get());
596 implicit_tenant_context
.reset(); // deletes
597 #ifdef WITH_RADOSGW_AMQP_ENDPOINT
598 rgw::amqp::shutdown();
600 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
601 rgw::kafka::shutdown();
603 rgw_perf_stop(g_ceph_context
);
604 ratelimiter
.reset(); // deletes--ensure this happens before we destruct
605 } /* AppMain::shutdown */