]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_main.cc
update sources to v12.1.1
[ceph.git] / ceph / src / rgw / rgw_main.cc
CommitLineData
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#include <stdlib.h>
5#include <stdio.h>
6#include <string.h>
7#include <stdarg.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <fcntl.h>
11#include <errno.h>
12#include <signal.h>
13
14#include <curl/curl.h>
15
16#include <boost/intrusive_ptr.hpp>
17
18#include "acconfig.h"
19
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"
224ce89b 29#include "include/stringify.h"
7c673cae
FG
30#include "rgw_common.h"
31#include "rgw_rados.h"
32#include "rgw_user.h"
33#include "rgw_period_pusher.h"
34#include "rgw_realm_reloader.h"
35#include "rgw_rest.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"
50#include "rgw_log.h"
51#include "rgw_tools.h"
52#include "rgw_resolve.h"
53
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 */
60
61#include <map>
62#include <string>
63#include <vector>
64#include <atomic>
65
66#include "include/types.h"
67#include "common/BackTrace.h"
68
69#ifdef HAVE_SYS_PRCTL_H
70#include <sys/prctl.h>
71#endif
72
73#define dout_subsys ceph_subsys_rgw
74
75using namespace std;
76
77static sig_t sighandler_alrm;
78
79class RGWProcess;
80
81static int signal_fd[2] = {0, 0};
82static std::atomic<int64_t> disable_signal_fd = { 0 };
83
84void signal_shutdown()
85{
86 if (!disable_signal_fd) {
87 int val = 0;
88 int ret = write(signal_fd[0], (char *)&val, sizeof(val));
89 if (ret < 0) {
90 derr << "ERROR: " << __func__ << ": write() returned "
91 << cpp_strerror(errno) << dendl;
92 }
93 }
94}
95
96static void wait_shutdown()
97{
98 int val;
99 int r = safe_read_exact(signal_fd[1], &val, sizeof(val));
100 if (r < 0) {
101 derr << "safe_read_exact returned with error" << dendl;
102 }
103}
104
105static int signal_fd_init()
106{
107 return socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fd);
108}
109
110static void signal_fd_finalize()
111{
112 close(signal_fd[0]);
113 close(signal_fd[1]);
114}
115
116static void handle_sigterm(int signum)
117{
118 dout(1) << __func__ << dendl;
119#if defined(WITH_RADOSGW_FCGI_FRONTEND)
120 FCGX_ShutdownPending();
121#endif
122
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) {
127 signal_shutdown();
128
129 // safety net in case we get stuck doing an orderly shutdown.
130 uint64_t secs = g_ceph_context->_conf->rgw_exit_timeout_secs;
131 if (secs)
132 alarm(secs);
133 dout(1) << __func__ << " set alarm for " << secs << dendl;
134 }
135
136}
137
138static void godown_alarm(int signum)
139{
140 _exit(0);
141}
142
143#ifdef HAVE_CURL_MULTI_WAIT
144static void check_curl()
145{
146}
147#else
148static void check_curl()
149{
150 derr << "WARNING: libcurl doesn't support curl_multi_wait()" << dendl;
151 derr << "WARNING: cross zone / region transfer performance may be affected" << dendl;
152}
153#endif
154
155class C_InitTimeout : public Context {
156public:
157 C_InitTimeout() {}
158 void finish(int r) override {
159 derr << "Initialization timeout, failed to initialize" << dendl;
160 exit(1);
161 }
162};
163
164static int usage()
165{
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();
176
177 return 0;
178}
179
180static RGWRESTMgr *set_logging(RGWRESTMgr *mgr)
181{
182 mgr->set_logging(true);
183 return mgr;
184}
185
31f18b77
FG
186static RGWRESTMgr *rest_filter(RGWRados *store, int dialect, RGWRESTMgr *orig)
187{
188 RGWSyncModuleInstanceRef sync_module = store->get_sync_module();
189 return sync_module->get_rest_filter(dialect, orig);
190}
191
7c673cae
FG
192RGWRealmReloader *preloader = NULL;
193
194static void reloader_handler(int signum)
195{
196 if (preloader) {
197 bufferlist bl;
198 bufferlist::iterator p = bl.begin();
199 preloader->handle_notify(RGWRealmNotify::Reload, p);
200 }
201 sighup_handler(signum);
202}
203
204/*
205 * start up the RADOS connection and then handle HTTP messages as they come in
206 */
207#ifdef BUILDING_FOR_EMBEDDED
208extern "C" int cephd_rgw(int argc, const char **argv)
209#else
210int main(int argc, const char **argv)
211#endif
212{
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) {
217 int err = errno;
218 cout << "failed to redirect stderr to stdout: " << cpp_strerror(err)
219 << std::endl;
220 return ENOSYS;
221 }
222
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");
227
228 vector<const char*> args;
229 argv_to_vec(argc, argv, args);
230 env_to_vec(args);
231
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,
235 flags);
236
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()) {
31f18b77 242 frontends.push_back("civetweb");
7c673cae
FG
243 }
244 for (list<string>::iterator iter = frontends.begin(); iter != frontends.end(); ++iter) {
245 string& f = *iter;
246
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;
258 }
259 }
260 }
261
262 RGWFrontendConfig *config = new RGWFrontendConfig(f);
263 int r = config->init();
264 if (r < 0) {
265 delete config;
266 cerr << "ERROR: failed to init config: " << f << std::endl;
267 return EINVAL;
268 }
269
270 configs.push_back(config);
271
272 string framework = config->get_framework();
273 fe_map.insert(pair<string, RGWFrontendConfig*>(framework, config));
274 }
275
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);
283
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)) {
286 usage();
287 return 0;
288 }
289 }
290
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);
296 }
297 if (g_conf->rgw_period_root_pool.empty()) {
298 g_conf->set_val_or_die("rgw_period_root_pool", root_pool);
299 }
300 if (g_conf->rgw_realm_root_pool.empty()) {
301 g_conf->set_val_or_die("rgw_realm_root_pool", root_pool);
302 }
303 }
304
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());
308 }
309
310 check_curl();
311
312 if (g_conf->daemonize) {
313 global_init_daemonize(g_ceph_context);
314 }
315 Mutex mutex("main");
316 SafeTimer init_timer(g_ceph_context, mutex);
317 init_timer.init();
318 mutex.Lock();
319 init_timer.add_event_after(g_conf->rgw_init_timeout, new C_InitTimeout);
320 mutex.Unlock();
321
322 // Enable the perf counter before starting the service thread
323 g_ceph_context->enable_perf_counter();
324
325 common_init_finish(g_ceph_context);
326
327 int r = rgw_tools_init(g_ceph_context);
328 if (r < 0) {
329 derr << "ERROR: unable to initialize rgw tools" << dendl;
330 return -r;
331 }
332
333 rgw_init_resolver();
334
335 curl_global_init(CURL_GLOBAL_ALL);
336
337#if defined(WITH_RADOSGW_FCGI_FRONTEND)
338 FCGX_Init();
339#endif
340
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,
31f18b77 343 g_conf->rgw_run_sync_thread, g_conf->rgw_dynamic_resharding);
7c673cae
FG
344 if (!store) {
345 mutex.Lock();
346 init_timer.cancel_all_events();
347 init_timer.shutdown();
348 mutex.Unlock();
349
350 derr << "Couldn't init storage provider (RADOS)" << dendl;
351 return EIO;
352 }
353 r = rgw_perf_start(g_ceph_context);
354 if (r < 0) {
355 derr << "ERROR: failed starting rgw perf" << dendl;
356 return -r;
357 }
358
359 rgw_rest_init(g_ceph_context, store, store->get_zonegroup());
360
361 mutex.Lock();
362 init_timer.cancel_all_events();
363 init_timer.shutdown();
364 mutex.Unlock();
365
366 rgw_user_init(store);
367 rgw_bucket_init(store->meta_mgr);
368 rgw_log_usage_init(g_ceph_context, store);
369
370 RGWREST rest;
371
372 list<string> apis;
373
374 get_str_list(g_conf->rgw_enable_apis, apis);
375
376 map<string, bool> apis_map;
377 for (list<string>::iterator li = apis.begin(); li != apis.end(); ++li) {
378 apis_map[*li] = true;
379 }
380
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) {
31f18b77
FG
387 rest.register_default_mgr(set_logging(rest_filter(store, RGW_REST_S3,
388 new RGWRESTMgr_S3(s3website_enabled))));
7c673cae
FG
389 } else {
390 derr << "Cannot have the S3 or S3 Website enabled together with "
391 << "Swift API placed in the root of hierarchy" << dendl;
392 return EINVAL;
393 }
394 }
395
396 if (apis_map.count("swift") > 0) {
397 RGWRESTMgr_SWIFT* const swift_resource = new RGWRESTMgr_SWIFT;
398
399 if (! g_conf->rgw_cross_domain_policy.empty()) {
400 swift_resource->register_resource("crossdomain.xml",
401 set_logging(new RGWRESTMgr_SWIFT_CrossDomain));
402 }
403
404 swift_resource->register_resource("healthcheck",
405 set_logging(new RGWRESTMgr_SWIFT_HealthCheck));
406
407 swift_resource->register_resource("info",
408 set_logging(new RGWRESTMgr_SWIFT_Info));
409
410 if (! swift_at_root) {
411 rest.register_resource(g_conf->rgw_swift_url_prefix,
31f18b77
FG
412 set_logging(rest_filter(store, RGW_REST_SWIFT,
413 swift_resource)));
7c673cae
FG
414 } else {
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;
419 }
420
421 rest.register_default_mgr(set_logging(swift_resource));
422 }
423 }
424
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));
428 }
429
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);
435
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);
444 }
445
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);
450
451 /* Header custom behavior */
452 rest.register_x_headers(g_conf->rgw_log_http_headers);
453
454 OpsLogSocket *olog = NULL;
455
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);
459 }
460
461 r = signal_fd_init();
462 if (r < 0) {
463 derr << "ERROR: unable to initialize signal fds" << dendl;
464 exit(1);
465 }
466
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);
473
224ce89b
WB
474 map<string, string> service_map_meta;
475 service_map_meta["pid"] = stringify(getpid());
476
7c673cae
FG
477 list<RGWFrontend *> fes;
478
224ce89b
WB
479 int fe_count = 0;
480
7c673cae 481 for (multimap<string, RGWFrontendConfig *>::iterator fiter = fe_map.begin();
224ce89b 482 fiter != fe_map.end(); ++fiter, ++fe_count) {
7c673cae
FG
483 RGWFrontendConfig *config = fiter->second;
484 string framework = config->get_framework();
485 RGWFrontend *fe = NULL;
486
487 if (framework == "civetweb" || framework == "mongoose") {
224ce89b 488 framework = "civetweb";
7c673cae
FG
489 std::string uri_prefix;
490 config->get_val("prefix", "", &uri_prefix);
491
492 RGWProcessEnv env = { store, &rest, olog, 0, uri_prefix, auth_registry };
493
494 fe = new RGWCivetWebFrontend(env, config);
495 }
496 else if (framework == "loadgen") {
497 int port;
498 config->get_val("port", 80, &port);
499 std::string uri_prefix;
500 config->get_val("prefix", "", &uri_prefix);
501
502 RGWProcessEnv env = { store, &rest, olog, port, uri_prefix, auth_registry };
503
504 fe = new RGWLoadGenFrontend(env, config);
505 }
506#if defined(WITH_RADOSGW_BEAST_FRONTEND)
507 else if ((framework == "beast") &&
508 cct->check_experimental_feature_enabled("rgw-beast-frontend")) {
509 int port;
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);
515 }
516#endif /* WITH_RADOSGW_BEAST_FRONTEND */
517#if defined(WITH_RADOSGW_FCGI_FRONTEND)
518 else if (framework == "fastcgi" || framework == "fcgi") {
224ce89b 519 framework = "fastcgi";
7c673cae
FG
520 std::string uri_prefix;
521 config->get_val("prefix", "", &uri_prefix);
522 RGWProcessEnv fcgi_pe = { store, &rest, olog, 0, uri_prefix, auth_registry };
523
524 fe = new RGWFCGXFrontend(fcgi_pe, config);
525 }
526#endif /* WITH_RADOSGW_FCGI_FRONTEND */
527
224ce89b
WB
528 service_map_meta["frontend_type#" + stringify(fe_count)] = framework;
529 service_map_meta["frontend_config#" + stringify(fe_count)] = config->get_config();
530
7c673cae
FG
531 if (fe == NULL) {
532 dout(0) << "WARNING: skipping unknown framework: " << framework << dendl;
533 continue;
534 }
535
536 dout(0) << "starting handler: " << fiter->first << dendl;
537 int r = fe->init();
538 if (r < 0) {
539 derr << "ERROR: failed initializing frontend" << dendl;
540 return -r;
541 }
542 r = fe->run();
543 if (r < 0) {
544 derr << "ERROR: failed run" << dendl;
545 return -r;
546 }
547
548 fes.push_back(fe);
549 }
550
224ce89b
WB
551 r = store->register_to_service_map("rgw", service_map_meta);
552 if (r < 0) {
553 derr << "ERROR: failed to register to service map: " << cpp_strerror(-r) << dendl;
554
555 /* ignore error */
556 }
557
558
7c673cae
FG
559 // add a watcher to respond to realm configuration changes
560 RGWPeriodPusher pusher(store);
561 RGWFrontendPauser pauser(fes, &pusher);
224ce89b 562 RGWRealmReloader reloader(store, service_map_meta, &pauser);
7c673cae
FG
563
564 preloader = &reloader;
565
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);
569
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;
573 }
574#endif
575
576 wait_shutdown();
577
578 derr << "shutting down" << dendl;
579
580 for (list<RGWFrontend *>::iterator liter = fes.begin(); liter != fes.end();
581 ++liter) {
582 RGWFrontend *fe = *liter;
583 fe->stop();
584 }
585
586 for (list<RGWFrontend *>::iterator liter = fes.begin(); liter != fes.end();
587 ++liter) {
588 RGWFrontend *fe = *liter;
589 fe->join();
590 delete fe;
591 }
592
593 for (list<RGWFrontendConfig *>::iterator liter = configs.begin();
594 liter != configs.end(); ++liter) {
595 RGWFrontendConfig *fec = *liter;
596 delete fec;
597 }
598
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();
604
605 rgw_log_usage_finalize();
606
607 delete olog;
608
609 RGWStoreManager::close_storage(store);
610
611 rgw_tools_cleanup();
612 rgw_shutdown_resolver();
613 curl_global_cleanup();
614
615 rgw_perf_stop(g_ceph_context);
616
617 dout(1) << "final shutdown" << dendl;
618
619 signal_fd_finalize();
620
621 return 0;
622}