]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_main.cc
f4e57be713a9268fd22fc28072775ff46955d0d8
[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
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"
29 #include "rgw_common.h"
30 #include "rgw_rados.h"
31 #include "rgw_user.h"
32 #include "rgw_period_pusher.h"
33 #include "rgw_realm_reloader.h"
34 #include "rgw_rest.h"
35 #include "rgw_rest_s3.h"
36 #include "rgw_rest_swift.h"
37 #include "rgw_rest_admin.h"
38 #include "rgw_rest_usage.h"
39 #include "rgw_rest_user.h"
40 #include "rgw_rest_bucket.h"
41 #include "rgw_rest_metadata.h"
42 #include "rgw_rest_log.h"
43 #include "rgw_rest_opstate.h"
44 #include "rgw_replica_log.h"
45 #include "rgw_rest_replica_log.h"
46 #include "rgw_rest_config.h"
47 #include "rgw_rest_realm.h"
48 #include "rgw_swift_auth.h"
49 #include "rgw_log.h"
50 #include "rgw_tools.h"
51 #include "rgw_resolve.h"
52
53 #include "rgw_request.h"
54 #include "rgw_process.h"
55 #include "rgw_frontend.h"
56 #if defined(WITH_RADOSGW_BEAST_FRONTEND)
57 #include "rgw_asio_frontend.h"
58 #endif /* WITH_RADOSGW_BEAST_FRONTEND */
59
60 #include <map>
61 #include <string>
62 #include <vector>
63 #include <atomic>
64
65 #include "include/types.h"
66 #include "common/BackTrace.h"
67
68 #ifdef HAVE_SYS_PRCTL_H
69 #include <sys/prctl.h>
70 #endif
71
72 #define dout_subsys ceph_subsys_rgw
73
74 using namespace std;
75
76 static sig_t sighandler_alrm;
77
78 class RGWProcess;
79
80 static int signal_fd[2] = {0, 0};
81 static std::atomic<int64_t> disable_signal_fd = { 0 };
82
83 void signal_shutdown()
84 {
85 if (!disable_signal_fd) {
86 int val = 0;
87 int ret = write(signal_fd[0], (char *)&val, sizeof(val));
88 if (ret < 0) {
89 derr << "ERROR: " << __func__ << ": write() returned "
90 << cpp_strerror(errno) << dendl;
91 }
92 }
93 }
94
95 static void wait_shutdown()
96 {
97 int val;
98 int r = safe_read_exact(signal_fd[1], &val, sizeof(val));
99 if (r < 0) {
100 derr << "safe_read_exact returned with error" << dendl;
101 }
102 }
103
104 static int signal_fd_init()
105 {
106 return socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fd);
107 }
108
109 static void signal_fd_finalize()
110 {
111 close(signal_fd[0]);
112 close(signal_fd[1]);
113 }
114
115 static void handle_sigterm(int signum)
116 {
117 dout(1) << __func__ << dendl;
118 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
119 FCGX_ShutdownPending();
120 #endif
121
122 // send a signal to make fcgi's accept(2) wake up. unfortunately the
123 // initial signal often isn't sufficient because we race with accept's
124 // check of the flag wet by ShutdownPending() above.
125 if (signum != SIGUSR1) {
126 signal_shutdown();
127
128 // safety net in case we get stuck doing an orderly shutdown.
129 uint64_t secs = g_ceph_context->_conf->rgw_exit_timeout_secs;
130 if (secs)
131 alarm(secs);
132 dout(1) << __func__ << " set alarm for " << secs << dendl;
133 }
134
135 }
136
137 static void godown_alarm(int signum)
138 {
139 _exit(0);
140 }
141
142 #ifdef HAVE_CURL_MULTI_WAIT
143 static void check_curl()
144 {
145 }
146 #else
147 static void check_curl()
148 {
149 derr << "WARNING: libcurl doesn't support curl_multi_wait()" << dendl;
150 derr << "WARNING: cross zone / region transfer performance may be affected" << dendl;
151 }
152 #endif
153
154 class C_InitTimeout : public Context {
155 public:
156 C_InitTimeout() {}
157 void finish(int r) override {
158 derr << "Initialization timeout, failed to initialize" << dendl;
159 exit(1);
160 }
161 };
162
163 static int usage()
164 {
165 cerr << "usage: radosgw [options...]" << std::endl;
166 cerr << "options:\n";
167 cerr << " --rgw-region=<region> region in which radosgw runs\n";
168 cerr << " --rgw-zone=<zone> zone in which radosgw runs\n";
169 cerr << " --rgw-socket-path=<path> specify a unix domain socket path\n";
170 cerr << " -m monaddress[:port] connect to specified monitor\n";
171 cerr << " --keyring=<path> path to radosgw keyring\n";
172 cerr << " --logfile=<logfile> file to log debug output\n";
173 cerr << " --debug-rgw=<log-level>/<memory-level> set radosgw debug level\n";
174 generic_server_usage();
175
176 return 0;
177 }
178
179 static RGWRESTMgr *set_logging(RGWRESTMgr *mgr)
180 {
181 mgr->set_logging(true);
182 return mgr;
183 }
184
185 static RGWRESTMgr *rest_filter(RGWRados *store, int dialect, RGWRESTMgr *orig)
186 {
187 RGWSyncModuleInstanceRef sync_module = store->get_sync_module();
188 return sync_module->get_rest_filter(dialect, orig);
189 }
190
191 RGWRealmReloader *preloader = NULL;
192
193 static void reloader_handler(int signum)
194 {
195 if (preloader) {
196 bufferlist bl;
197 bufferlist::iterator p = bl.begin();
198 preloader->handle_notify(RGWRealmNotify::Reload, p);
199 }
200 sighup_handler(signum);
201 }
202
203 /*
204 * start up the RADOS connection and then handle HTTP messages as they come in
205 */
206 #ifdef BUILDING_FOR_EMBEDDED
207 extern "C" int cephd_rgw(int argc, const char **argv)
208 #else
209 int main(int argc, const char **argv)
210 #endif
211 {
212 // dout() messages will be sent to stderr, but FCGX wants messages on stdout
213 // Redirect stderr to stdout.
214 TEMP_FAILURE_RETRY(close(STDERR_FILENO));
215 if (TEMP_FAILURE_RETRY(dup2(STDOUT_FILENO, STDERR_FILENO)) < 0) {
216 int err = errno;
217 cout << "failed to redirect stderr to stdout: " << cpp_strerror(err)
218 << std::endl;
219 return ENOSYS;
220 }
221
222 /* alternative default for module */
223 vector<const char *> def_args;
224 def_args.push_back("--debug-rgw=1/5");
225 def_args.push_back("--keyring=$rgw_data/keyring");
226
227 vector<const char*> args;
228 argv_to_vec(argc, argv, args);
229 env_to_vec(args);
230
231 // First, let's determine which frontends are configured.
232 int flags = CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS;
233 global_pre_init(&def_args, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_DAEMON,
234 flags);
235
236 list<string> frontends;
237 get_str_list(g_conf->rgw_frontends, ",", frontends);
238 multimap<string, RGWFrontendConfig *> fe_map;
239 list<RGWFrontendConfig *> configs;
240 if (frontends.empty()) {
241 frontends.push_back("civetweb");
242 }
243 for (list<string>::iterator iter = frontends.begin(); iter != frontends.end(); ++iter) {
244 string& f = *iter;
245
246 if (f.find("civetweb") != string::npos) {
247 // If civetweb is configured as a frontend, prevent global_init() from
248 // dropping permissions by setting the appropriate flag.
249 flags |= CINIT_FLAG_DEFER_DROP_PRIVILEGES;
250 if (f.find("port") != string::npos) {
251 // check for the most common ws problems
252 if ((f.find("port=") == string::npos) ||
253 (f.find("port= ") != string::npos)) {
254 derr << "WARNING: civetweb frontend config found unexpected spacing around 'port' "
255 << "(ensure civetweb port parameter has the form 'port=80' with no spaces "
256 << "before or after '=')" << dendl;
257 }
258 }
259 }
260
261 RGWFrontendConfig *config = new RGWFrontendConfig(f);
262 int r = config->init();
263 if (r < 0) {
264 delete config;
265 cerr << "ERROR: failed to init config: " << f << std::endl;
266 return EINVAL;
267 }
268
269 configs.push_back(config);
270
271 string framework = config->get_framework();
272 fe_map.insert(pair<string, RGWFrontendConfig*>(framework, config));
273 }
274
275 // Now that we've determined which frontend(s) to use, continue with global
276 // initialization. Passing false as the final argument ensures that
277 // global_pre_init() is not invoked twice.
278 // claim the reference and release it after subsequent destructors have fired
279 auto cct = global_init(&def_args, args, CEPH_ENTITY_TYPE_CLIENT,
280 CODE_ENVIRONMENT_DAEMON,
281 flags, "rgw_data", false);
282
283 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ++i) {
284 if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
285 usage();
286 return 0;
287 }
288 }
289
290 // maintain existing region root pool for new multisite objects
291 if (!g_conf->rgw_region_root_pool.empty()) {
292 const char *root_pool = g_conf->rgw_region_root_pool.c_str();
293 if (g_conf->rgw_zonegroup_root_pool.empty()) {
294 g_conf->set_val_or_die("rgw_zonegroup_root_pool", root_pool);
295 }
296 if (g_conf->rgw_period_root_pool.empty()) {
297 g_conf->set_val_or_die("rgw_period_root_pool", root_pool);
298 }
299 if (g_conf->rgw_realm_root_pool.empty()) {
300 g_conf->set_val_or_die("rgw_realm_root_pool", root_pool);
301 }
302 }
303
304 // for region -> zonegroup conversion (must happen before common_init_finish())
305 if (!g_conf->rgw_region.empty() && g_conf->rgw_zonegroup.empty()) {
306 g_conf->set_val_or_die("rgw_zonegroup", g_conf->rgw_region.c_str());
307 }
308
309 check_curl();
310
311 if (g_conf->daemonize) {
312 global_init_daemonize(g_ceph_context);
313 }
314 Mutex mutex("main");
315 SafeTimer init_timer(g_ceph_context, mutex);
316 init_timer.init();
317 mutex.Lock();
318 init_timer.add_event_after(g_conf->rgw_init_timeout, new C_InitTimeout);
319 mutex.Unlock();
320
321 // Enable the perf counter before starting the service thread
322 g_ceph_context->enable_perf_counter();
323
324 common_init_finish(g_ceph_context);
325
326 int r = rgw_tools_init(g_ceph_context);
327 if (r < 0) {
328 derr << "ERROR: unable to initialize rgw tools" << dendl;
329 return -r;
330 }
331
332 rgw_init_resolver();
333
334 curl_global_init(CURL_GLOBAL_ALL);
335
336 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
337 FCGX_Init();
338 #endif
339
340 RGWRados *store = RGWStoreManager::get_storage(g_ceph_context,
341 g_conf->rgw_enable_gc_threads, g_conf->rgw_enable_lc_threads, g_conf->rgw_enable_quota_threads,
342 g_conf->rgw_run_sync_thread, g_conf->rgw_dynamic_resharding);
343 if (!store) {
344 mutex.Lock();
345 init_timer.cancel_all_events();
346 init_timer.shutdown();
347 mutex.Unlock();
348
349 derr << "Couldn't init storage provider (RADOS)" << dendl;
350 return EIO;
351 }
352 r = rgw_perf_start(g_ceph_context);
353 if (r < 0) {
354 derr << "ERROR: failed starting rgw perf" << dendl;
355 return -r;
356 }
357
358 rgw_rest_init(g_ceph_context, store, store->get_zonegroup());
359
360 mutex.Lock();
361 init_timer.cancel_all_events();
362 init_timer.shutdown();
363 mutex.Unlock();
364
365 rgw_user_init(store);
366 rgw_bucket_init(store->meta_mgr);
367 rgw_log_usage_init(g_ceph_context, store);
368
369 RGWREST rest;
370
371 list<string> apis;
372
373 get_str_list(g_conf->rgw_enable_apis, apis);
374
375 map<string, bool> apis_map;
376 for (list<string>::iterator li = apis.begin(); li != apis.end(); ++li) {
377 apis_map[*li] = true;
378 }
379
380 // S3 website mode is a specialization of S3
381 const bool s3website_enabled = apis_map.count("s3website") > 0;
382 // Swift API entrypoint could placed in the root instead of S3
383 const bool swift_at_root = g_conf->rgw_swift_url_prefix == "/";
384 if (apis_map.count("s3") > 0 || s3website_enabled) {
385 if (! swift_at_root) {
386 rest.register_default_mgr(set_logging(rest_filter(store, RGW_REST_S3,
387 new RGWRESTMgr_S3(s3website_enabled))));
388 } else {
389 derr << "Cannot have the S3 or S3 Website enabled together with "
390 << "Swift API placed in the root of hierarchy" << dendl;
391 return EINVAL;
392 }
393 }
394
395 if (apis_map.count("swift") > 0) {
396 RGWRESTMgr_SWIFT* const swift_resource = new RGWRESTMgr_SWIFT;
397
398 if (! g_conf->rgw_cross_domain_policy.empty()) {
399 swift_resource->register_resource("crossdomain.xml",
400 set_logging(new RGWRESTMgr_SWIFT_CrossDomain));
401 }
402
403 swift_resource->register_resource("healthcheck",
404 set_logging(new RGWRESTMgr_SWIFT_HealthCheck));
405
406 swift_resource->register_resource("info",
407 set_logging(new RGWRESTMgr_SWIFT_Info));
408
409 if (! swift_at_root) {
410 rest.register_resource(g_conf->rgw_swift_url_prefix,
411 set_logging(rest_filter(store, RGW_REST_SWIFT,
412 swift_resource)));
413 } else {
414 if (store->get_zonegroup().zones.size() > 1) {
415 derr << "Placing Swift API in the root of URL hierarchy while running"
416 << " multi-site configuration requires another instance of RadosGW"
417 << " with S3 API enabled!" << dendl;
418 }
419
420 rest.register_default_mgr(set_logging(swift_resource));
421 }
422 }
423
424 if (apis_map.count("swift_auth") > 0) {
425 rest.register_resource(g_conf->rgw_swift_auth_entry,
426 set_logging(new RGWRESTMgr_SWIFT_Auth));
427 }
428
429 if (apis_map.count("admin") > 0) {
430 RGWRESTMgr_Admin *admin_resource = new RGWRESTMgr_Admin;
431 admin_resource->register_resource("usage", new RGWRESTMgr_Usage);
432 admin_resource->register_resource("user", new RGWRESTMgr_User);
433 admin_resource->register_resource("bucket", new RGWRESTMgr_Bucket);
434
435 /*Registering resource for /admin/metadata */
436 admin_resource->register_resource("metadata", new RGWRESTMgr_Metadata);
437 admin_resource->register_resource("log", new RGWRESTMgr_Log);
438 admin_resource->register_resource("opstate", new RGWRESTMgr_Opstate);
439 admin_resource->register_resource("replica_log", new RGWRESTMgr_ReplicaLog);
440 admin_resource->register_resource("config", new RGWRESTMgr_Config);
441 admin_resource->register_resource("realm", new RGWRESTMgr_Realm);
442 rest.register_resource(g_conf->rgw_admin_entry, admin_resource);
443 }
444
445 /* Initialize the registry of auth strategies which will coordinate
446 * the dynamic reconfiguration. */
447 auto auth_registry = \
448 rgw::auth::StrategyRegistry::create(g_ceph_context, store);
449
450 /* Header custom behavior */
451 rest.register_x_headers(g_conf->rgw_log_http_headers);
452
453 OpsLogSocket *olog = NULL;
454
455 if (!g_conf->rgw_ops_log_socket_path.empty()) {
456 olog = new OpsLogSocket(g_ceph_context, g_conf->rgw_ops_log_data_backlog);
457 olog->init(g_conf->rgw_ops_log_socket_path);
458 }
459
460 r = signal_fd_init();
461 if (r < 0) {
462 derr << "ERROR: unable to initialize signal fds" << dendl;
463 exit(1);
464 }
465
466 init_async_signal_handler();
467 register_async_signal_handler(SIGHUP, reloader_handler);
468 register_async_signal_handler(SIGTERM, handle_sigterm);
469 register_async_signal_handler(SIGINT, handle_sigterm);
470 register_async_signal_handler(SIGUSR1, handle_sigterm);
471 sighandler_alrm = signal(SIGALRM, godown_alarm);
472
473 list<RGWFrontend *> fes;
474
475 for (multimap<string, RGWFrontendConfig *>::iterator fiter = fe_map.begin();
476 fiter != fe_map.end(); ++fiter) {
477 RGWFrontendConfig *config = fiter->second;
478 string framework = config->get_framework();
479 RGWFrontend *fe = NULL;
480
481 if (framework == "civetweb" || framework == "mongoose") {
482 std::string uri_prefix;
483 config->get_val("prefix", "", &uri_prefix);
484
485 RGWProcessEnv env = { store, &rest, olog, 0, uri_prefix, auth_registry };
486
487 fe = new RGWCivetWebFrontend(env, config);
488 }
489 else if (framework == "loadgen") {
490 int port;
491 config->get_val("port", 80, &port);
492 std::string uri_prefix;
493 config->get_val("prefix", "", &uri_prefix);
494
495 RGWProcessEnv env = { store, &rest, olog, port, uri_prefix, auth_registry };
496
497 fe = new RGWLoadGenFrontend(env, config);
498 }
499 #if defined(WITH_RADOSGW_BEAST_FRONTEND)
500 else if ((framework == "beast") &&
501 cct->check_experimental_feature_enabled("rgw-beast-frontend")) {
502 int port;
503 config->get_val("port", 80, &port);
504 std::string uri_prefix;
505 config->get_val("prefix", "", &uri_prefix);
506 RGWProcessEnv env{ store, &rest, olog, port, uri_prefix, auth_registry };
507 fe = new RGWAsioFrontend(env);
508 }
509 #endif /* WITH_RADOSGW_BEAST_FRONTEND */
510 #if defined(WITH_RADOSGW_FCGI_FRONTEND)
511 else if (framework == "fastcgi" || framework == "fcgi") {
512 std::string uri_prefix;
513 config->get_val("prefix", "", &uri_prefix);
514 RGWProcessEnv fcgi_pe = { store, &rest, olog, 0, uri_prefix, auth_registry };
515
516 fe = new RGWFCGXFrontend(fcgi_pe, config);
517 }
518 #endif /* WITH_RADOSGW_FCGI_FRONTEND */
519
520 if (fe == NULL) {
521 dout(0) << "WARNING: skipping unknown framework: " << framework << dendl;
522 continue;
523 }
524
525 dout(0) << "starting handler: " << fiter->first << dendl;
526 int r = fe->init();
527 if (r < 0) {
528 derr << "ERROR: failed initializing frontend" << dendl;
529 return -r;
530 }
531 r = fe->run();
532 if (r < 0) {
533 derr << "ERROR: failed run" << dendl;
534 return -r;
535 }
536
537 fes.push_back(fe);
538 }
539
540 // add a watcher to respond to realm configuration changes
541 RGWPeriodPusher pusher(store);
542 RGWFrontendPauser pauser(fes, &pusher);
543 RGWRealmReloader reloader(store, &pauser);
544
545 preloader = &reloader;
546
547 RGWRealmWatcher realm_watcher(g_ceph_context, store->realm);
548 realm_watcher.add_watcher(RGWRealmNotify::Reload, reloader);
549 realm_watcher.add_watcher(RGWRealmNotify::ZonesNeedPeriod, pusher);
550
551 #if defined(HAVE_SYS_PRCTL_H)
552 if (prctl(PR_SET_DUMPABLE, 1) == -1) {
553 cerr << "warning: unable to set dumpable flag: " << cpp_strerror(errno) << std::endl;
554 }
555 #endif
556
557 wait_shutdown();
558
559 derr << "shutting down" << dendl;
560
561 for (list<RGWFrontend *>::iterator liter = fes.begin(); liter != fes.end();
562 ++liter) {
563 RGWFrontend *fe = *liter;
564 fe->stop();
565 }
566
567 for (list<RGWFrontend *>::iterator liter = fes.begin(); liter != fes.end();
568 ++liter) {
569 RGWFrontend *fe = *liter;
570 fe->join();
571 delete fe;
572 }
573
574 for (list<RGWFrontendConfig *>::iterator liter = configs.begin();
575 liter != configs.end(); ++liter) {
576 RGWFrontendConfig *fec = *liter;
577 delete fec;
578 }
579
580 unregister_async_signal_handler(SIGHUP, reloader_handler);
581 unregister_async_signal_handler(SIGTERM, handle_sigterm);
582 unregister_async_signal_handler(SIGINT, handle_sigterm);
583 unregister_async_signal_handler(SIGUSR1, handle_sigterm);
584 shutdown_async_signal_handler();
585
586 rgw_log_usage_finalize();
587
588 delete olog;
589
590 RGWStoreManager::close_storage(store);
591
592 rgw_tools_cleanup();
593 rgw_shutdown_resolver();
594 curl_global_cleanup();
595
596 rgw_perf_stop(g_ceph_context);
597
598 dout(1) << "final shutdown" << dendl;
599
600 signal_fd_finalize();
601
602 return 0;
603 }