1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
14 #include <curl/curl.h>
16 #include <boost/intrusive_ptr.hpp>
20 #include "common/ceph_argparse.h"
21 #include "global/global_init.h"
22 #include "global/signal_handler.h"
23 #include "common/config.h"
24 #include "common/errno.h"
25 #include "common/Timer.h"
26 #include "common/safe_io.h"
27 #include "include/compat.h"
28 #include "include/str_list.h"
29 #include "include/stringify.h"
30 #include "rgw_common.h"
31 #include "rgw_rados.h"
33 #include "rgw_period_pusher.h"
34 #include "rgw_realm_reloader.h"
36 #include "rgw_rest_s3.h"
37 #include "rgw_rest_swift.h"
38 #include "rgw_rest_admin.h"
39 #include "rgw_rest_usage.h"
40 #include "rgw_rest_user.h"
41 #include "rgw_rest_bucket.h"
42 #include "rgw_rest_metadata.h"
43 #include "rgw_rest_log.h"
44 #include "rgw_rest_opstate.h"
45 #include "rgw_replica_log.h"
46 #include "rgw_rest_replica_log.h"
47 #include "rgw_rest_config.h"
48 #include "rgw_rest_realm.h"
49 #include "rgw_swift_auth.h"
51 #include "rgw_tools.h"
52 #include "rgw_resolve.h"
54 #include "rgw_request.h"
55 #include "rgw_process.h"
56 #include "rgw_frontend.h"
57 #if defined(WITH_RADOSGW_BEAST_FRONTEND)
58 #include "rgw_asio_frontend.h"
59 #endif /* WITH_RADOSGW_BEAST_FRONTEND */
66 #include "include/types.h"
67 #include "common/BackTrace.h"
69 #ifdef HAVE_SYS_PRCTL_H
70 #include <sys/prctl.h>
73 #define dout_subsys ceph_subsys_rgw
77 static sig_t sighandler_alrm
;
81 static int signal_fd
[2] = {0, 0};
82 static std::atomic
<int64_t> disable_signal_fd
= { 0 };
84 void signal_shutdown()
86 if (!disable_signal_fd
) {
88 int ret
= write(signal_fd
[0], (char *)&val
, sizeof(val
));
90 derr
<< "ERROR: " << __func__
<< ": write() returned "
91 << cpp_strerror(errno
) << dendl
;
96 static void wait_shutdown()
99 int r
= safe_read_exact(signal_fd
[1], &val
, sizeof(val
));
101 derr
<< "safe_read_exact returned with error" << dendl
;
105 static int signal_fd_init()
107 return socketpair(AF_UNIX
, SOCK_STREAM
, 0, signal_fd
);
110 static void signal_fd_finalize()
116 static void handle_sigterm(int signum
)
118 dout(1) << __func__
<< dendl
;
119 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
120 FCGX_ShutdownPending();
123 // send a signal to make fcgi's accept(2) wake up. unfortunately the
124 // initial signal often isn't sufficient because we race with accept's
125 // check of the flag wet by ShutdownPending() above.
126 if (signum
!= SIGUSR1
) {
129 // safety net in case we get stuck doing an orderly shutdown.
130 uint64_t secs
= g_ceph_context
->_conf
->rgw_exit_timeout_secs
;
133 dout(1) << __func__
<< " set alarm for " << secs
<< dendl
;
138 static void godown_alarm(int signum
)
143 #ifdef HAVE_CURL_MULTI_WAIT
144 static void check_curl()
148 static void check_curl()
150 derr
<< "WARNING: libcurl doesn't support curl_multi_wait()" << dendl
;
151 derr
<< "WARNING: cross zone / region transfer performance may be affected" << dendl
;
155 class C_InitTimeout
: public Context
{
158 void finish(int r
) override
{
159 derr
<< "Initialization timeout, failed to initialize" << dendl
;
166 cerr
<< "usage: radosgw [options...]" << std::endl
;
167 cerr
<< "options:\n";
168 cerr
<< " --rgw-region=<region> region in which radosgw runs\n";
169 cerr
<< " --rgw-zone=<zone> zone in which radosgw runs\n";
170 cerr
<< " --rgw-socket-path=<path> specify a unix domain socket path\n";
171 cerr
<< " -m monaddress[:port] connect to specified monitor\n";
172 cerr
<< " --keyring=<path> path to radosgw keyring\n";
173 cerr
<< " --logfile=<logfile> file to log debug output\n";
174 cerr
<< " --debug-rgw=<log-level>/<memory-level> set radosgw debug level\n";
175 generic_server_usage();
180 static RGWRESTMgr
*set_logging(RGWRESTMgr
*mgr
)
182 mgr
->set_logging(true);
186 static RGWRESTMgr
*rest_filter(RGWRados
*store
, int dialect
, RGWRESTMgr
*orig
)
188 RGWSyncModuleInstanceRef sync_module
= store
->get_sync_module();
189 return sync_module
->get_rest_filter(dialect
, orig
);
192 RGWRealmReloader
*preloader
= NULL
;
194 static void reloader_handler(int signum
)
198 bufferlist::iterator p
= bl
.begin();
199 preloader
->handle_notify(RGWRealmNotify::Reload
, p
);
201 sighup_handler(signum
);
205 * start up the RADOS connection and then handle HTTP messages as they come in
207 #ifdef BUILDING_FOR_EMBEDDED
208 extern "C" int cephd_rgw(int argc
, const char **argv
)
210 int main(int argc
, const char **argv
)
213 // dout() messages will be sent to stderr, but FCGX wants messages on stdout
214 // Redirect stderr to stdout.
215 TEMP_FAILURE_RETRY(close(STDERR_FILENO
));
216 if (TEMP_FAILURE_RETRY(dup2(STDOUT_FILENO
, STDERR_FILENO
)) < 0) {
218 cout
<< "failed to redirect stderr to stdout: " << cpp_strerror(err
)
223 /* alternative default for module */
224 vector
<const char *> def_args
;
225 def_args
.push_back("--debug-rgw=1/5");
226 def_args
.push_back("--keyring=$rgw_data/keyring");
228 vector
<const char*> args
;
229 argv_to_vec(argc
, argv
, args
);
232 // First, let's determine which frontends are configured.
233 int flags
= CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS
;
234 global_pre_init(&def_args
, args
, CEPH_ENTITY_TYPE_CLIENT
, CODE_ENVIRONMENT_DAEMON
,
237 list
<string
> frontends
;
238 get_str_list(g_conf
->rgw_frontends
, ",", frontends
);
239 multimap
<string
, RGWFrontendConfig
*> fe_map
;
240 list
<RGWFrontendConfig
*> configs
;
241 if (frontends
.empty()) {
242 frontends
.push_back("civetweb");
244 for (list
<string
>::iterator iter
= frontends
.begin(); iter
!= frontends
.end(); ++iter
) {
247 if (f
.find("civetweb") != string::npos
) {
248 // If civetweb is configured as a frontend, prevent global_init() from
249 // dropping permissions by setting the appropriate flag.
250 flags
|= CINIT_FLAG_DEFER_DROP_PRIVILEGES
;
251 if (f
.find("port") != string::npos
) {
252 // check for the most common ws problems
253 if ((f
.find("port=") == string::npos
) ||
254 (f
.find("port= ") != string::npos
)) {
255 derr
<< "WARNING: civetweb frontend config found unexpected spacing around 'port' "
256 << "(ensure civetweb port parameter has the form 'port=80' with no spaces "
257 << "before or after '=')" << dendl
;
262 RGWFrontendConfig
*config
= new RGWFrontendConfig(f
);
263 int r
= config
->init();
266 cerr
<< "ERROR: failed to init config: " << f
<< std::endl
;
270 configs
.push_back(config
);
272 string framework
= config
->get_framework();
273 fe_map
.insert(pair
<string
, RGWFrontendConfig
*>(framework
, config
));
276 // Now that we've determined which frontend(s) to use, continue with global
277 // initialization. Passing false as the final argument ensures that
278 // global_pre_init() is not invoked twice.
279 // claim the reference and release it after subsequent destructors have fired
280 auto cct
= global_init(&def_args
, args
, CEPH_ENTITY_TYPE_CLIENT
,
281 CODE_ENVIRONMENT_DAEMON
,
282 flags
, "rgw_data", false);
284 for (std::vector
<const char*>::iterator i
= args
.begin(); i
!= args
.end(); ++i
) {
285 if (ceph_argparse_flag(args
, i
, "-h", "--help", (char*)NULL
)) {
291 // maintain existing region root pool for new multisite objects
292 if (!g_conf
->rgw_region_root_pool
.empty()) {
293 const char *root_pool
= g_conf
->rgw_region_root_pool
.c_str();
294 if (g_conf
->rgw_zonegroup_root_pool
.empty()) {
295 g_conf
->set_val_or_die("rgw_zonegroup_root_pool", root_pool
);
297 if (g_conf
->rgw_period_root_pool
.empty()) {
298 g_conf
->set_val_or_die("rgw_period_root_pool", root_pool
);
300 if (g_conf
->rgw_realm_root_pool
.empty()) {
301 g_conf
->set_val_or_die("rgw_realm_root_pool", root_pool
);
305 // for region -> zonegroup conversion (must happen before common_init_finish())
306 if (!g_conf
->rgw_region
.empty() && g_conf
->rgw_zonegroup
.empty()) {
307 g_conf
->set_val_or_die("rgw_zonegroup", g_conf
->rgw_region
.c_str());
312 if (g_conf
->daemonize
) {
313 global_init_daemonize(g_ceph_context
);
316 SafeTimer
init_timer(g_ceph_context
, mutex
);
319 init_timer
.add_event_after(g_conf
->rgw_init_timeout
, new C_InitTimeout
);
322 // Enable the perf counter before starting the service thread
323 g_ceph_context
->enable_perf_counter();
325 common_init_finish(g_ceph_context
);
327 int r
= rgw_tools_init(g_ceph_context
);
329 derr
<< "ERROR: unable to initialize rgw tools" << dendl
;
335 curl_global_init(CURL_GLOBAL_ALL
);
337 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
341 RGWRados
*store
= RGWStoreManager::get_storage(g_ceph_context
,
342 g_conf
->rgw_enable_gc_threads
, g_conf
->rgw_enable_lc_threads
, g_conf
->rgw_enable_quota_threads
,
343 g_conf
->rgw_run_sync_thread
, g_conf
->rgw_dynamic_resharding
);
346 init_timer
.cancel_all_events();
347 init_timer
.shutdown();
350 derr
<< "Couldn't init storage provider (RADOS)" << dendl
;
353 r
= rgw_perf_start(g_ceph_context
);
355 derr
<< "ERROR: failed starting rgw perf" << dendl
;
359 rgw_rest_init(g_ceph_context
, store
, store
->get_zonegroup());
362 init_timer
.cancel_all_events();
363 init_timer
.shutdown();
366 rgw_user_init(store
);
367 rgw_bucket_init(store
->meta_mgr
);
368 rgw_log_usage_init(g_ceph_context
, store
);
374 get_str_list(g_conf
->rgw_enable_apis
, apis
);
376 map
<string
, bool> apis_map
;
377 for (list
<string
>::iterator li
= apis
.begin(); li
!= apis
.end(); ++li
) {
378 apis_map
[*li
] = true;
381 // S3 website mode is a specialization of S3
382 const bool s3website_enabled
= apis_map
.count("s3website") > 0;
383 // Swift API entrypoint could placed in the root instead of S3
384 const bool swift_at_root
= g_conf
->rgw_swift_url_prefix
== "/";
385 if (apis_map
.count("s3") > 0 || s3website_enabled
) {
386 if (! swift_at_root
) {
387 rest
.register_default_mgr(set_logging(rest_filter(store
, RGW_REST_S3
,
388 new RGWRESTMgr_S3(s3website_enabled
))));
390 derr
<< "Cannot have the S3 or S3 Website enabled together with "
391 << "Swift API placed in the root of hierarchy" << dendl
;
396 if (apis_map
.count("swift") > 0) {
397 RGWRESTMgr_SWIFT
* const swift_resource
= new RGWRESTMgr_SWIFT
;
399 if (! g_conf
->rgw_cross_domain_policy
.empty()) {
400 swift_resource
->register_resource("crossdomain.xml",
401 set_logging(new RGWRESTMgr_SWIFT_CrossDomain
));
404 swift_resource
->register_resource("healthcheck",
405 set_logging(new RGWRESTMgr_SWIFT_HealthCheck
));
407 swift_resource
->register_resource("info",
408 set_logging(new RGWRESTMgr_SWIFT_Info
));
410 if (! swift_at_root
) {
411 rest
.register_resource(g_conf
->rgw_swift_url_prefix
,
412 set_logging(rest_filter(store
, RGW_REST_SWIFT
,
415 if (store
->get_zonegroup().zones
.size() > 1) {
416 derr
<< "Placing Swift API in the root of URL hierarchy while running"
417 << " multi-site configuration requires another instance of RadosGW"
418 << " with S3 API enabled!" << dendl
;
421 rest
.register_default_mgr(set_logging(swift_resource
));
425 if (apis_map
.count("swift_auth") > 0) {
426 rest
.register_resource(g_conf
->rgw_swift_auth_entry
,
427 set_logging(new RGWRESTMgr_SWIFT_Auth
));
430 if (apis_map
.count("admin") > 0) {
431 RGWRESTMgr_Admin
*admin_resource
= new RGWRESTMgr_Admin
;
432 admin_resource
->register_resource("usage", new RGWRESTMgr_Usage
);
433 admin_resource
->register_resource("user", new RGWRESTMgr_User
);
434 admin_resource
->register_resource("bucket", new RGWRESTMgr_Bucket
);
436 /*Registering resource for /admin/metadata */
437 admin_resource
->register_resource("metadata", new RGWRESTMgr_Metadata
);
438 admin_resource
->register_resource("log", new RGWRESTMgr_Log
);
439 admin_resource
->register_resource("opstate", new RGWRESTMgr_Opstate
);
440 admin_resource
->register_resource("replica_log", new RGWRESTMgr_ReplicaLog
);
441 admin_resource
->register_resource("config", new RGWRESTMgr_Config
);
442 admin_resource
->register_resource("realm", new RGWRESTMgr_Realm
);
443 rest
.register_resource(g_conf
->rgw_admin_entry
, admin_resource
);
446 /* Initialize the registry of auth strategies which will coordinate
447 * the dynamic reconfiguration. */
448 auto auth_registry
= \
449 rgw::auth::StrategyRegistry::create(g_ceph_context
, store
);
451 /* Header custom behavior */
452 rest
.register_x_headers(g_conf
->rgw_log_http_headers
);
454 OpsLogSocket
*olog
= NULL
;
456 if (!g_conf
->rgw_ops_log_socket_path
.empty()) {
457 olog
= new OpsLogSocket(g_ceph_context
, g_conf
->rgw_ops_log_data_backlog
);
458 olog
->init(g_conf
->rgw_ops_log_socket_path
);
461 r
= signal_fd_init();
463 derr
<< "ERROR: unable to initialize signal fds" << dendl
;
467 init_async_signal_handler();
468 register_async_signal_handler(SIGHUP
, reloader_handler
);
469 register_async_signal_handler(SIGTERM
, handle_sigterm
);
470 register_async_signal_handler(SIGINT
, handle_sigterm
);
471 register_async_signal_handler(SIGUSR1
, handle_sigterm
);
472 sighandler_alrm
= signal(SIGALRM
, godown_alarm
);
474 map
<string
, string
> service_map_meta
;
475 service_map_meta
["pid"] = stringify(getpid());
477 list
<RGWFrontend
*> fes
;
481 for (multimap
<string
, RGWFrontendConfig
*>::iterator fiter
= fe_map
.begin();
482 fiter
!= fe_map
.end(); ++fiter
, ++fe_count
) {
483 RGWFrontendConfig
*config
= fiter
->second
;
484 string framework
= config
->get_framework();
485 RGWFrontend
*fe
= NULL
;
487 if (framework
== "civetweb" || framework
== "mongoose") {
488 framework
= "civetweb";
489 std::string uri_prefix
;
490 config
->get_val("prefix", "", &uri_prefix
);
492 RGWProcessEnv env
= { store
, &rest
, olog
, 0, uri_prefix
, auth_registry
};
494 fe
= new RGWCivetWebFrontend(env
, config
);
496 else if (framework
== "loadgen") {
498 config
->get_val("port", 80, &port
);
499 std::string uri_prefix
;
500 config
->get_val("prefix", "", &uri_prefix
);
502 RGWProcessEnv env
= { store
, &rest
, olog
, port
, uri_prefix
, auth_registry
};
504 fe
= new RGWLoadGenFrontend(env
, config
);
506 #if defined(WITH_RADOSGW_BEAST_FRONTEND)
507 else if ((framework
== "beast") &&
508 cct
->check_experimental_feature_enabled("rgw-beast-frontend")) {
510 config
->get_val("port", 80, &port
);
511 std::string uri_prefix
;
512 config
->get_val("prefix", "", &uri_prefix
);
513 RGWProcessEnv env
{ store
, &rest
, olog
, port
, uri_prefix
, auth_registry
};
514 fe
= new RGWAsioFrontend(env
);
516 #endif /* WITH_RADOSGW_BEAST_FRONTEND */
517 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
518 else if (framework
== "fastcgi" || framework
== "fcgi") {
519 framework
= "fastcgi";
520 std::string uri_prefix
;
521 config
->get_val("prefix", "", &uri_prefix
);
522 RGWProcessEnv fcgi_pe
= { store
, &rest
, olog
, 0, uri_prefix
, auth_registry
};
524 fe
= new RGWFCGXFrontend(fcgi_pe
, config
);
526 #endif /* WITH_RADOSGW_FCGI_FRONTEND */
528 service_map_meta
["frontend_type#" + stringify(fe_count
)] = framework
;
529 service_map_meta
["frontend_config#" + stringify(fe_count
)] = config
->get_config();
532 dout(0) << "WARNING: skipping unknown framework: " << framework
<< dendl
;
536 dout(0) << "starting handler: " << fiter
->first
<< dendl
;
539 derr
<< "ERROR: failed initializing frontend" << dendl
;
544 derr
<< "ERROR: failed run" << dendl
;
551 r
= store
->register_to_service_map("rgw", service_map_meta
);
553 derr
<< "ERROR: failed to register to service map: " << cpp_strerror(-r
) << dendl
;
559 // add a watcher to respond to realm configuration changes
560 RGWPeriodPusher
pusher(store
);
561 RGWFrontendPauser
pauser(fes
, &pusher
);
562 RGWRealmReloader
reloader(store
, service_map_meta
, &pauser
);
564 preloader
= &reloader
;
566 RGWRealmWatcher
realm_watcher(g_ceph_context
, store
->realm
);
567 realm_watcher
.add_watcher(RGWRealmNotify::Reload
, reloader
);
568 realm_watcher
.add_watcher(RGWRealmNotify::ZonesNeedPeriod
, pusher
);
570 #if defined(HAVE_SYS_PRCTL_H)
571 if (prctl(PR_SET_DUMPABLE
, 1) == -1) {
572 cerr
<< "warning: unable to set dumpable flag: " << cpp_strerror(errno
) << std::endl
;
578 derr
<< "shutting down" << dendl
;
580 for (list
<RGWFrontend
*>::iterator liter
= fes
.begin(); liter
!= fes
.end();
582 RGWFrontend
*fe
= *liter
;
586 for (list
<RGWFrontend
*>::iterator liter
= fes
.begin(); liter
!= fes
.end();
588 RGWFrontend
*fe
= *liter
;
593 for (list
<RGWFrontendConfig
*>::iterator liter
= configs
.begin();
594 liter
!= configs
.end(); ++liter
) {
595 RGWFrontendConfig
*fec
= *liter
;
599 unregister_async_signal_handler(SIGHUP
, reloader_handler
);
600 unregister_async_signal_handler(SIGTERM
, handle_sigterm
);
601 unregister_async_signal_handler(SIGINT
, handle_sigterm
);
602 unregister_async_signal_handler(SIGUSR1
, handle_sigterm
);
603 shutdown_async_signal_handler();
605 rgw_log_usage_finalize();
609 RGWStoreManager::close_storage(store
);
612 rgw_shutdown_resolver();
613 curl_global_cleanup();
615 rgw_perf_stop(g_ceph_context
);
617 dout(1) << "final shutdown" << dendl
;
619 signal_fd_finalize();