1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
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"
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"
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
44 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
45 #include "rgw_kafka.h"
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"
52 #include "services/svc_zone.h"
54 #ifdef HAVE_SYS_PRCTL_H
55 #include <sys/prctl.h>
58 #define dout_subsys ceph_subsys_rgw
61 TracepointProvider::Traits
rgw_op_tracepoint_traits("librgw_op_tp.so",
63 TracepointProvider::Traits
rgw_rados_tracepoint_traits("librgw_rados_tp.so",
67 static sig_t sighandler_alrm
;
71 static int signal_fd
[2] = {0, 0};
73 void signal_shutdown()
76 int ret
= write(signal_fd
[0], (char *)&val
, sizeof(val
));
78 derr
<< "ERROR: " << __func__
<< ": write() returned "
79 << cpp_strerror(errno
) << dendl
;
83 static void wait_shutdown()
86 int r
= safe_read_exact(signal_fd
[1], &val
, sizeof(val
));
88 derr
<< "safe_read_exact returned with error" << dendl
;
92 static int signal_fd_init()
94 return socketpair(AF_UNIX
, SOCK_STREAM
, 0, signal_fd
);
97 static void signal_fd_finalize()
103 static void handle_sigterm(int signum
)
105 dout(1) << __func__
<< dendl
;
106 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
107 FCGX_ShutdownPending();
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
) {
116 // safety net in case we get stuck doing an orderly shutdown.
117 uint64_t secs
= g_ceph_context
->_conf
->rgw_exit_timeout_secs
;
120 dout(1) << __func__
<< " set alarm for " << secs
<< dendl
;
125 static void godown_alarm(int signum
)
131 class C_InitTimeout
: public Context
{
134 void finish(int r
) override
{
135 derr
<< "Initialization timeout, failed to initialize" << dendl
;
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();
156 static RGWRESTMgr
*set_logging(RGWRESTMgr
*mgr
)
158 mgr
->set_logging(true);
162 static RGWRESTMgr
*rest_filter(RGWRados
*store
, int dialect
, RGWRESTMgr
*orig
)
164 RGWSyncModuleInstanceRef sync_module
= store
->get_sync_module();
166 return sync_module
->get_rest_filter(dialect
, orig
);
173 * start up the RADOS connection and then handle HTTP messages as they come in
175 int radosgw_Main(int argc
, const char **argv
)
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) {
182 cout
<< "failed to redirect stderr to stdout: " << cpp_strerror(err
)
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" }
194 vector
<const char*> args
;
195 argv_to_vec(argc
, argv
, args
);
197 cerr
<< argv
[0] << ": -h or --help for usage" << std::endl
;
200 if (ceph_argparse_need_usage(args
)) {
205 int flags
= CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS
;
207 &defaults
, args
, CEPH_ENTITY_TYPE_CLIENT
, CODE_ENVIRONMENT_DAEMON
,
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);
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");
228 for (list
<string
>::iterator iter
= frontends
.begin(); iter
!= frontends
.end(); ++iter
) {
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
;
246 RGWFrontendConfig
*config
= new RGWFrontendConfig(f
);
247 int r
= config
->init();
250 cerr
<< "ERROR: failed to init config: " << f
<< std::endl
;
254 configs
.push_back(config
);
256 string framework
= config
->get_framework();
257 fe_map
.insert(pair
<string
, RGWFrontendConfig
*>(framework
, config
));
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
;
264 if (numa_node
>= 0) {
265 int r
= get_numa_node_cpu_set(numa_node
, &numa_cpu_set_size
, &numa_cpu_set
);
267 dout(1) << __func__
<< " unable to determine rgw numa node " << numa_node
271 r
= set_cpu_affinity_all_threads(numa_cpu_set_size
, &numa_cpu_set
);
273 derr
<< __func__
<< " failed to set numa affinity: " << cpp_strerror(r
)
278 dout(1) << __func__
<< " not setting numa affinity" << dendl
;
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
);
287 if (g_conf()->rgw_period_root_pool
.empty()) {
288 g_conf().set_val_or_die("rgw_period_root_pool", root_pool
);
290 if (g_conf()->rgw_realm_root_pool
.empty()) {
291 g_conf().set_val_or_die("rgw_realm_root_pool", root_pool
);
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());
300 if (g_conf()->daemonize
) {
301 global_init_daemonize(g_ceph_context
);
303 ceph::mutex mutex
= ceph::make_mutex("main");
304 SafeTimer
init_timer(g_ceph_context
, mutex
);
307 init_timer
.add_event_after(g_conf()->rgw_init_timeout
, new C_InitTimeout
);
310 common_init_finish(g_ceph_context
);
312 init_async_signal_handler();
313 register_async_signal_handler(SIGHUP
, sighup_handler
);
315 TracepointProvider::initialize
<rgw_rados_tracepoint_traits
>(g_ceph_context
);
316 TracepointProvider::initialize
<rgw_op_tracepoint_traits
>(g_ceph_context
);
318 int r
= rgw_tools_init(g_ceph_context
);
320 derr
<< "ERROR: unable to initialize rgw tools" << dendl
;
325 rgw::curl::setup_curl(fe_map
);
326 rgw_http_client_init(g_ceph_context
);
328 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
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
);
342 init_timer
.cancel_all_events();
343 init_timer
.shutdown();
346 derr
<< "Couldn't init storage provider (RADOS)" << dendl
;
349 r
= rgw_perf_start(g_ceph_context
);
351 derr
<< "ERROR: failed starting rgw perf" << dendl
;
355 rgw_rest_init(g_ceph_context
, store
->svc()->zone
->get_zonegroup());
358 init_timer
.cancel_all_events();
359 init_timer
.shutdown();
362 rgw_log_usage_init(g_ceph_context
, store
->getRados());
368 get_str_list(g_conf()->rgw_enable_apis
, apis
);
370 map
<string
, bool> apis_map
;
371 for (list
<string
>::iterator li
= apis
.begin(); li
!= apis
.end(); ++li
) {
372 apis_map
[*li
] = true;
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
;
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
))));
393 derr
<< "Cannot have the S3 or S3 Website enabled together with "
394 << "Swift API placed in the root of hierarchy" << dendl
;
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
;
405 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
406 if (!rgw::kafka::init(cct
.get())) {
407 dout(1) << "ERROR: failed to initialize Kafka manager" << dendl
;
412 if (apis_map
.count("swift") > 0) {
413 RGWRESTMgr_SWIFT
* const swift_resource
= new RGWRESTMgr_SWIFT
;
415 if (! g_conf()->rgw_cross_domain_policy
.empty()) {
416 swift_resource
->register_resource("crossdomain.xml",
417 set_logging(new RGWRESTMgr_SWIFT_CrossDomain
));
420 swift_resource
->register_resource("healthcheck",
421 set_logging(new RGWRESTMgr_SWIFT_HealthCheck
));
423 swift_resource
->register_resource("info",
424 set_logging(new RGWRESTMgr_SWIFT_Info
));
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
,
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
;
437 rest
.register_default_mgr(set_logging(swift_resource
));
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
));
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
);
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
);
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
);
467 /* Header custom behavior */
468 rest
.register_x_headers(g_conf()->rgw_log_http_headers
);
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"
478 rgw::dmclock::SchedulerCtx sched_ctx
{cct
.get()};
480 OpsLogSocket
*olog
= NULL
;
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
);
487 r
= signal_fd_init();
489 derr
<< "ERROR: unable to initialize signal fds" << dendl
;
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
);
498 map
<string
, string
> service_map_meta
;
499 service_map_meta
["pid"] = stringify(getpid());
501 list
<RGWFrontend
*> fes
;
503 string frontend_defs_str
= g_conf().get_val
<string
>("rgw_frontend_defaults");
505 list
<string
> frontends_def
;
506 get_str_list(frontend_defs_str
, ",", frontends_def
);
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();
514 cerr
<< "ERROR: failed to init default config: " << f
<< std::endl
;
518 fe_def_map
[config
->get_framework()].reset(config
);
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();
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
);
533 RGWFrontend
*fe
= NULL
;
535 if (framework
== "civetweb" || framework
== "mongoose") {
536 framework
= "civetweb";
537 std::string uri_prefix
;
538 config
->get_val("prefix", "", &uri_prefix
);
540 RGWProcessEnv env
= { store
, &rest
, olog
, 0, uri_prefix
, auth_registry
};
541 //TODO: move all of scheduler initializations to frontends?
543 fe
= new RGWCivetWebFrontend(env
, config
, sched_ctx
);
545 else if (framework
== "loadgen") {
547 config
->get_val("port", 80, &port
);
548 std::string uri_prefix
;
549 config
->get_val("prefix", "", &uri_prefix
);
551 RGWProcessEnv env
= { store
, &rest
, olog
, port
, uri_prefix
, auth_registry
};
553 fe
= new RGWLoadGenFrontend(env
, config
);
555 #if defined(WITH_RADOSGW_BEAST_FRONTEND)
556 else if (framework
== "beast") {
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
);
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
};
572 fe
= new RGWFCGXFrontend(fcgi_pe
, config
);
574 #endif /* WITH_RADOSGW_FCGI_FRONTEND */
576 service_map_meta
["frontend_type#" + stringify(fe_count
)] = framework
;
577 service_map_meta
["frontend_config#" + stringify(fe_count
)] = config
->get_config();
580 dout(0) << "WARNING: skipping unknown framework: " << framework
<< dendl
;
584 dout(0) << "starting handler: " << fiter
->first
<< dendl
;
587 derr
<< "ERROR: failed initializing frontend" << dendl
;
592 derr
<< "ERROR: failed run" << dendl
;
599 r
= store
->getRados()->register_to_service_map("rgw", service_map_meta
);
601 derr
<< "ERROR: failed to register to service map: " << cpp_strerror(-r
) << dendl
;
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
);
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
);
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
;
624 derr
<< "shutting down" << dendl
;
626 for (list
<RGWFrontend
*>::iterator liter
= fes
.begin(); liter
!= fes
.end();
628 RGWFrontend
*fe
= *liter
;
632 for (list
<RGWFrontend
*>::iterator liter
= fes
.begin(); liter
!= fes
.end();
634 RGWFrontend
*fe
= *liter
;
639 for (list
<RGWFrontendConfig
*>::iterator liter
= configs
.begin();
640 liter
!= configs
.end(); ++liter
) {
641 RGWFrontendConfig
*fec
= *liter
;
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();
651 rgw_log_usage_finalize();
655 RGWStoreManager::close_storage(store
);
656 rgw::auth::s3::LDAPEngine::shutdown();
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();
665 #ifdef WITH_RADOSGW_KAFKA_ENDPOINT
666 rgw::kafka::shutdown();
669 rgw_perf_stop(g_ceph_context
);
671 dout(1) << "final shutdown" << dendl
;
673 signal_fd_finalize();
680 int radosgw_main(int argc
, const char** argv
)
682 return radosgw_Main(argc
, argv
);