]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
Merge remote-tracking branch 'origin/master' into wip-rgw-new-multisite
authorYehuda Sadeh <yehuda@redhat.com>
Fri, 19 Feb 2016 01:14:07 +0000 (17:14 -0800)
committerYehuda Sadeh <yehuda@redhat.com>
Fri, 19 Feb 2016 01:14:07 +0000 (17:14 -0800)
Signed-off-by: Yehuda Sadeh <yehuda@redhat.com>
Conflicts:
src/CMakeLists.txt
src/rgw/Makefile.am
src/rgw/rgw_admin.cc
src/rgw/rgw_common.h
src/rgw/rgw_main.cc
src/rgw/rgw_op.cc
src/rgw/rgw_rados.h
src/rgw/rgw_rest_s3.cc
src/test/Makefile-client.am

34 files changed:
1  2 
CMakeLists.txt
src/CMakeLists.txt
src/common/config_opts.h
src/rgw/Makefile.am
src/rgw/librgw.cc
src/rgw/rgw_admin.cc
src/rgw/rgw_bucket.cc
src/rgw/rgw_bucket.h
src/rgw/rgw_civetweb_frontend.cc
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_frontend.h
src/rgw/rgw_lib.h
src/rgw/rgw_log.cc
src/rgw/rgw_main.cc
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_process.h
src/rgw/rgw_rados.cc
src/rgw/rgw_rados.h
src/rgw/rgw_rest.cc
src/rgw/rgw_rest.h
src/rgw/rgw_rest_config.h
src/rgw/rgw_rest_log.h
src/rgw/rgw_rest_realm.cc
src/rgw/rgw_rest_realm.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/rgw/rgw_rest_swift.cc
src/rgw/rgw_user.cc
src/rgw/rgw_user.h
src/test/CMakeLists.txt
src/test/Makefile-client.am
src/test/cli/radosgw-admin/help.t

diff --cc CMakeLists.txt
Simple merge
index f30d949fa6243dff6f628feb2c0ca61dc387e4f1,4850e8c945b5bc386ca6a65883906268b8dd4229..d6bb021be27d83c93cff0928dc8222d22fab24a0
@@@ -1136,81 -1137,70 +1138,86 @@@ if(${WITH_RADOSGW}
      rgw/rgw_acl.cc
      rgw/rgw_acl_s3.cc
      rgw/rgw_acl_swift.cc
-     rgw/rgw_client_io.cc
-     rgw/rgw_fcgi.cc
-     rgw/rgw_xml.cc
-     rgw/rgw_usage.cc
-     rgw/rgw_json_enc.cc
-     rgw/rgw_user.cc
-     rgw/rgw_bucket.cc
-     rgw/rgw_tools.cc
-     rgw/rgw_rados.cc
-     rgw/rgw_http_client.cc
-     rgw/rgw_rest_client.cc
-     rgw/rgw_rest_conn.cc
-     rgw/rgw_op.cc
+     rgw/rgw_auth_s3.cc
      rgw/rgw_basic_types.cc
-     rgw/rgw_common.cc
+     rgw/rgw_bucket.cc
      rgw/rgw_cache.cc
+     rgw/rgw_client_io.cc
+     rgw/rgw_common.cc
+     rgw/rgw_cors.cc
+     rgw/rgw_cors_s3.cc
+     rgw/rgw_dencoder.cc
+     rgw/rgw_env.cc
+     rgw/rgw_fcgi.cc
      rgw/rgw_formats.cc
+     rgw/rgw_frontend.cc
+     rgw/rgw_gc.cc
+     rgw/rgw_http_client.cc
+     rgw/rgw_json_enc.cc
+     rgw/rgw_keystone.cc
+     rgw/rgw_loadgen.cc
      rgw/rgw_log.cc
+     rgw/rgw_metadata.cc
      rgw/rgw_multi.cc
-     rgw/rgw_policy_s3.cc
-     rgw/rgw_gc.cc
      rgw/rgw_multi_del.cc
-     rgw/rgw_env.cc
-     rgw/rgw_cors.cc
-     rgw/rgw_cors_s3.cc
-     rgw/rgw_auth_s3.cc
-     rgw/rgw_metadata.cc
-     rgw/rgw_replica_log.cc
-     rgw/rgw_keystone.cc
-     rgw/rgw_quota.cc
 +    rgw/rgw_sync.cc
 +    rgw/rgw_data_sync.cc
-     rgw/rgw_dencoder.cc
 +    rgw/rgw_period_history.cc
 +    rgw/rgw_period_puller.cc
 +    rgw/rgw_period_pusher.cc
 +    rgw/rgw_realm_reloader.cc
 +    rgw/rgw_realm_watcher.cc
 +    rgw/rgw_coroutine.cc
 +    rgw/rgw_cr_rados.cc
      rgw/rgw_object_expirer_core.cc
+     rgw/rgw_op.cc
+     rgw/rgw_os_lib.cc
+     rgw/rgw_policy_s3.cc
+     rgw/rgw_process.cc
+     rgw/rgw_quota.cc
+     rgw/rgw_rados.cc
+     rgw/rgw_replica_log.cc
+     rgw/rgw_request.cc
+     rgw/rgw_resolve.cc
+     rgw/rgw_rest_bucket.cc
+     rgw/rgw_rest.cc
+     rgw/rgw_rest_client.cc
+     rgw/rgw_rest_config.cc
+     rgw/rgw_rest_conn.cc
+     rgw/rgw_rest_log.cc
+     rgw/rgw_rest_metadata.cc
+     rgw/rgw_rest_opstate.cc
++    rgw/rgw_rest_realm.cc
+     rgw/rgw_rest_replica_log.cc
+     rgw/rgw_rest_s3.cc
+     rgw/rgw_rest_swift.cc
+     rgw/rgw_rest_usage.cc
+     rgw/rgw_rest_user.cc
+     rgw/rgw_swift_auth.cc
+     rgw/rgw_swift.cc
+     rgw/rgw_tools.cc
+     rgw/rgw_usage.cc
+     rgw/rgw_user.cc
      rgw/rgw_website.cc
-     rgw/rgw_xml_enc.cc)
+     rgw/rgw_xml.cc
+     rgw/rgw_xml_enc.cc
+     )
  
    add_library(rgw_a STATIC ${rgw_a_srcs})
 +  target_include_directories(rgw_a PUBLIC "${CMAKE_SOURCE_DIR}/src/civetweb/include")
 +  target_link_libraries(rgw_a librados cls_rgw_client cls_refcount_client
 +    cls_log_client cls_statelog_client cls_timeindex_client cls_version_client
 +    cls_replica_log_client cls_user_client curl global expat)
  
 -  include_directories("${CMAKE_SOURCE_DIR}/src/civetweb/include")
 +  if(HAVE_BOOST_ASIO_COROUTINE)
 +    target_compile_definitions(rgw_a PUBLIC "HAVE_BOOST_ASIO_COROUTINE")
 +  endif()
  
    set(radosgw_srcs
-     rgw/rgw_resolve.cc
-     rgw/rgw_rest.cc
-     rgw/rgw_rest_swift.cc
-     rgw/rgw_rest_s3.cc
-     rgw/rgw_rest_usage.cc
-     rgw/rgw_rest_user.cc
-     rgw/rgw_rest_bucket.cc
-     rgw/rgw_rest_metadata.cc
-     rgw/rgw_replica_log.cc
-     rgw/rgw_rest_log.cc
-     rgw/rgw_rest_opstate.cc
-     rgw/rgw_rest_replica_log.cc
-     rgw/rgw_rest_config.cc
-     rgw/rgw_rest_realm.cc
-     rgw/rgw_http_client.cc
-     rgw/rgw_swift.cc
-     rgw/rgw_swift_auth.cc
-     rgw/rgw_loadgen.cc
+     rgw/rgw_fcgi_process.cc
+     rgw/rgw_loadgen_process.cc
      rgw/rgw_civetweb.cc
+     rgw/rgw_civetweb_frontend.cc
      rgw/rgw_civetweb_log.cc
      civetweb/src/civetweb.c
      rgw/rgw_main.cc)
index a36bcc003c83cac774c73561aefa848bbe8e6358,65774387ab9025a986b4a5bc34a09bfbde0655e5..160ed36cb018e79de5d4c69a58435d415f4d6949
@@@ -1201,19 -1202,25 +1202,33 @@@ OPTION(rgw_thread_pool_size, OPT_INT, 1
  OPTION(rgw_num_control_oids, OPT_INT, 8)
  OPTION(rgw_num_rados_handles, OPT_U32, 1)
  
+ /* The following are tunables for caches of RGW NFS (and other file
+  * client) objects.
+  *
+  * The file handle cache is a partitioned hash table
+  * (fhcache_partitions), each with a closed hash part and backing
+  * b-tree mapping.  The number of partions is expected to be a small
+  * prime, the cache size something larger but less than 5K, the total
+  * size of the cache is n_part * cache_size.
+  */
+ OPTION(rgw_nfs_lru_lanes, OPT_INT, 5)
+ OPTION(rgw_nfs_lru_lane_hiwat, OPT_INT, 911)
+ OPTION(rgw_nfs_fhcache_partitions, OPT_INT, 3)
+ OPTION(rgw_nfs_fhcache_size, OPT_INT, 2017) /* 3*2017=6051 */
  OPTION(rgw_zone, OPT_STR, "") // zone name
  OPTION(rgw_zone_root_pool, OPT_STR, ".rgw.root")    // pool where zone specific info is stored
 +OPTION(rgw_default_zone_info_oid, OPT_STR, "default.zone")  // oid where default zone info is stored
  OPTION(rgw_region, OPT_STR, "") // region name
 -OPTION(rgw_region_root_pool, OPT_STR, ".rgw.root")  // pool where all region info is stored
  OPTION(rgw_default_region_info_oid, OPT_STR, "default.region")  // oid where default region info is stored
 +OPTION(rgw_zonegroup, OPT_STR, "") // zone group name
 +OPTION(rgw_zonegroup_root_pool, OPT_STR, ".rgw.root")  // pool where all zone group info is stored
 +OPTION(rgw_default_zonegroup_info_oid, OPT_STR, "default.zonegroup")  // oid where default zone group info is stored
 +OPTION(rgw_realm, OPT_STR, "") // realm name
 +OPTION(rgw_realm_root_pool, OPT_STR, ".rgw.root")  // pool where all realm info is stored
 +OPTION(rgw_default_realm_info_oid, OPT_STR, "default.realm")  // oid where default realm info is stored
 +OPTION(rgw_period_root_pool, OPT_STR, ".rgw.root")  // pool where all period info is stored
 +OPTION(rgw_period_latest_epoch_info_oid, OPT_STR, ".latest_epoch") // oid where current period info is stored
  OPTION(rgw_log_nonexistent_bucket, OPT_BOOL, false)
  OPTION(rgw_log_object_name, OPT_STR, "%Y-%m-%d-%H-%i-%n")      // man date to see codes (a subset are supported)
  OPTION(rgw_log_object_name_utc, OPT_BOOL, false)
index 808a31773b61c7252d47d6697c8325be72f71f67,775ee986c3d6b8e63c26f964a4a8ece66657883b..cb301df911d46476e585574cf306793d3288c464
@@@ -25,52 -16,64 +24,75 @@@ librgw_la_SOURCES = 
        rgw/rgw_acl.cc \
        rgw/rgw_acl_s3.cc \
        rgw/rgw_acl_swift.cc \
-       rgw/rgw_client_io.cc \
 -      rgw/rgw_auth_s3.cc \
 +      rgw/rgw_coroutine.cc \
 +      rgw/rgw_cr_rados.cc \
-       rgw/rgw_fcgi.cc \
-       rgw/rgw_xml.cc \
-       rgw/rgw_usage.cc \
-       rgw/rgw_json_enc.cc \
-       rgw/rgw_xml_enc.cc \
-       rgw/rgw_user.cc \
-       rgw/rgw_bucket.cc\
 +      rgw/rgw_tools.cc \
-       rgw/rgw_rados.cc \
-       rgw/rgw_http_client.cc \
-       rgw/rgw_rest_client.cc \
-       rgw/rgw_rest_conn.cc \
-       rgw/rgw_op.cc \
        rgw/rgw_basic_types.cc \
-       rgw/rgw_common.cc \
+       rgw/rgw_bucket.cc \
        rgw/rgw_cache.cc \
+       rgw/rgw_client_io.cc \
+       rgw/rgw_common.cc \
+       rgw/rgw_cors.cc \
+       rgw/rgw_cors_s3.cc \
+       rgw/rgw_dencoder.cc \
+       rgw/rgw_env.cc \
+       rgw/rgw_fcgi.cc \
        rgw/rgw_formats.cc \
+       rgw/rgw_frontend.cc \
+       rgw/rgw_gc.cc \
+       rgw/rgw_http_client.cc \
+       rgw/rgw_json_enc.cc \
+       rgw/rgw_keystone.cc \
+       rgw/rgw_loadgen.cc \
        rgw/rgw_log.cc \
+       rgw/rgw_metadata.cc \
        rgw/rgw_multi.cc \
-       rgw/rgw_policy_s3.cc \
-       rgw/rgw_gc.cc \
        rgw/rgw_multi_del.cc \
-       rgw/rgw_env.cc \
-       rgw/rgw_cors.cc \
-       rgw/rgw_cors_s3.cc \
 +      rgw/rgw_auth_s3.cc \
-       rgw/rgw_metadata.cc \
-       rgw/rgw_replica_log.cc \
-       rgw/rgw_keystone.cc \
-       rgw/rgw_quota.cc \
-       rgw/rgw_dencoder.cc \
 +      rgw/rgw_period_history.cc \
 +      rgw/rgw_period_puller.cc \
 +      rgw/rgw_period_pusher.cc \
 +      rgw/rgw_realm_reloader.cc \
 +      rgw/rgw_realm_watcher.cc \
-       rgw/rgw_object_expirer_core.cc \
-       rgw/rgw_website.cc \
 +      rgw/rgw_sync.cc \
-       rgw/rgw_data_sync.cc
++      rgw/rgw_data_sync.cc \
+       rgw/rgw_object_expirer_core.cc \
+       rgw/rgw_op.cc \
+       rgw/rgw_os_lib.cc \
+       rgw/rgw_policy_s3.cc \
+       rgw/rgw_process.cc \
+       rgw/rgw_quota.cc \
+       rgw/rgw_rados.cc \
+       rgw/rgw_replica_log.cc \
+       rgw/rgw_request.cc \
+       rgw/rgw_resolve.cc \
+       rgw/rgw_rest_bucket.cc \
+       rgw/rgw_rest.cc \
+       rgw/rgw_rest_client.cc \
+       rgw/rgw_rest_config.cc \
+       rgw/rgw_rest_conn.cc \
+       rgw/rgw_rest_log.cc \
+       rgw/rgw_rest_metadata.cc \
+       rgw/rgw_rest_opstate.cc \
++      rgw/rgw_rest_realm.cc \
+       rgw/rgw_rest_replica_log.cc \
+       rgw/rgw_rest_s3.cc \
+       rgw/rgw_rest_swift.cc \
+       rgw/rgw_rest_usage.cc \
+       rgw/rgw_rest_user.cc \
+       rgw/rgw_swift_auth.cc \
+       rgw/rgw_swift.cc \
 -      rgw/rgw_tools.cc \
+       rgw/rgw_usage.cc \
+       rgw/rgw_user.cc \
+       rgw/rgw_file.cc \
+       rgw/librgw.cc \
+       rgw/rgw_xml.cc \
+       rgw/rgw_xml_enc.cc \
+       rgw/rgw_website.cc
  
- librgw_la_CXXFLAGS = -Woverloaded-virtual ${AM_CXXFLAGS}
- noinst_LTLIBRARIES += librgw.la
+ librgw_la_CXXFLAGS = -Woverloaded-virtual -fPIC -I$(srcdir)/xxHash \
+       ${AM_CXXFLAGS}
++# noinst_LTLIBRARIES += librgw.la
  
  LIBRGW_DEPS += \
        $(LIBRADOS) \
index f9b39aeba59e217b7286c66e465c542eb7770e86,7deaecc06110c482d75d27c09898c9e8647ce74b..ada07af3653abb891ece04f644238fe6c7493015
  
  #define dout_subsys ceph_subsys_rgw
  
- int librgw_create(librgw_t *rgw, const char * const id)
- {
-   CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT);
-   if (id) {
-     iparams.name.set(CEPH_ENTITY_TYPE_CLIENT, id);
-   }
-   CephContext *cct = common_preinit(iparams, CODE_ENVIRONMENT_LIBRARY, 0,
-                                   "rgw_data");
-   cct->_conf->set_val("log_to_stderr", "false"); // quiet by default
-   cct->_conf->set_val("err_to_stderr", "true"); // quiet by default
-   cct->_conf->parse_env(); // environment variables override
-   cct->_conf->apply_changes(NULL);
-   common_init_finish(cct);
-   *rgw = cct;
-   return 0;
- }
+ bool global_stop = false;
  
- int librgw_acl_bin2xml(librgw_t rgw, const char *bin, int bin_len, char **xml)
- {
-   try {
-     // convert to bufferlist
-     bufferlist bl;
-     bl.append(bin, bin_len);
-     // convert to RGWAccessControlPolicy
-     RGWAccessControlPolicy_S3 acl((CephContext *)rgw);
-     bufferlist::iterator bli(bl.begin());
-     acl.decode(bli);
-     // convert to XML stringstream
-     stringstream ss;
-     acl.to_xml(ss);
-     // convert to XML C string
-     *xml = strdup(ss.str().c_str());
-     if (!*xml)
-       return -ENOBUFS;
-     return 0;
-   }
-   catch (const std::exception &e) {
-     lderr(rgw) << "librgw_acl_bin2xml: caught exception " << e.what() << dendl;
-     return -2000;
+ namespace rgw {
+   using std::string;
+   static std::mutex librgw_mtx;
+   RGWLib rgwlib;
+   class C_InitTimeout : public Context {
+   public:
+     C_InitTimeout() {}
+     void finish(int r) {
+       derr << "Initialization timeout, failed to initialize" << dendl;
+       exit(1);
+     }
+   };
+   void RGWLibProcess::checkpoint()
+   {
+     m_tp.drain(&req_wq);
    }
-   catch (...) {
-     lderr(rgw) << "librgw_acl_bin2xml: caught unknown exception " << dendl;
-     return -2000;
+   void RGWLibProcess::run()
+   {
+     while (! shutdown) {
+       lsubdout(cct, rgw, 5) << "RGWLibProcess GC" << dendl;
+       unique_lock uniq(mtx);
+     restart:
+       int cur_gen = gen;
+       for (auto iter = mounted_fs.begin(); iter != mounted_fs.end();
+          ++iter) {
+       RGWLibFS* fs = iter->first->ref();
+       uniq.unlock();
+       fs->gc();
+       fs->rele();
+       uniq.lock();
+       if (cur_gen != gen)
+         goto restart; /* invalidated */
+       }
+       uniq.unlock();
+       std::this_thread::sleep_for(std::chrono::seconds(120));
+     }
    }
- }
  
- void librgw_free_xml(librgw_t rgw, char *xml)
- {
-   free(xml);
- }
+   void RGWLibProcess::handle_request(RGWRequest* r)
+   {
+     /*
+      * invariant: valid requests are derived from RGWLibRequst
+      */
+     RGWLibRequest* req = static_cast<RGWLibRequest*>(r);
  
- int librgw_acl_xml2bin(librgw_t rgw, const char *xml, char **bin, int *bin_len)
- {
-   char *bin_ = NULL;
-   try {
-     RGWACLXMLParser_S3 parser((CephContext *)rgw);
-     if (!parser.init()) {
-       return -1000;
+     // XXX move RGWLibIO and timing setup into process_request
+ #if 0 /* XXX */
+     utime_t tm = ceph_clock_now(NULL);
+ #endif
+     RGWLibIO io_ctx;
+     int ret = process_request(req, &io_ctx);
+     if (ret < 0) {
+       /* we don't really care about return code */
+       dout(20) << "process_request() returned " << ret << dendl;
+     }
+     delete req;
+   } /* handle_request */
+   int RGWLibProcess::process_request(RGWLibRequest* req)
+   {
+     // XXX move RGWLibIO and timing setup into process_request
+ #if 0 /* XXX */
+     utime_t tm = ceph_clock_now(NULL);
+ #endif
+     RGWLibIO io_ctx;
+     int ret = process_request(req, &io_ctx);
+     if (ret < 0) {
+       /* we don't really care about return code */
+       dout(20) << "process_request() returned " << ret << dendl;
      }
-     if (!parser.parse(xml, strlen(xml), true)) {
+     return ret;
+   } /* process_request */
+   static inline void abort_req(struct req_state *s, RGWOp *op, int err_no)
+   {
+     if (!s)
+       return;
+     /* XXX the dump_errno and dump_bucket_from_state behaviors in
+      * the abort_early (rgw_rest.cc) might be valuable, but aren't
+      * safe to call presently as they return HTTP data */
+     perfcounter->inc(l_rgw_failed_req);
+   } /* abort_req */
+   int RGWLibProcess::process_request(RGWLibRequest* req, RGWLibIO* io)
+   {
+     int ret = 0;
+     bool should_log = true; // XXX
+     dout(1) << "====== " << __func__
+           << " starting new request req=" << hex << req << dec
+           << " ======" << dendl;
+     /*
+      * invariant: valid requests are derived from RGWOp--well-formed
+      * requests should have assigned RGWRequest::op in their descendant
+      * constructor--if not, the compiler can find it, at the cost of
+      * a runtime check
+      */
+     RGWOp *op = (req->op) ? req->op : dynamic_cast<RGWOp*>(req);
+     if (! op) {
+       dout(1) << "failed to derive cognate RGWOp (invalid op?)" << dendl;
        return -EINVAL;
      }
-     RGWAccessControlPolicy_S3 *policy =
-       (RGWAccessControlPolicy_S3 *)parser.find_first("AccessControlPolicy");
-     if (!policy) {
-       return -1001;
+     io->init(req->cct);
+     perfcounter->inc(l_rgw_req);
+     RGWEnv& rgw_env = io->get_env();
+     /* XXX
+      * until major refactoring of req_state and req_info, we need
+      * to build their RGWEnv boilerplate from the RGWLibRequest,
+      * pre-staging any strings (HTTP_HOST) that provoke a crash when
+      * not found
+      */
+     /* XXX for now, use "";  could be a legit hostname, or, in future,
+      * perhaps a tenant (Yehuda) */
+     rgw_env.set("HTTP_HOST", "");
+     /* XXX and -then- bloat up req_state with string copies from it */
+     struct req_state rstate(req->cct, &rgw_env, req->get_user());
+     struct req_state *s = &rstate;
+     // XXX fix this
+     s->cio = io;
+     RGWObjectCtx rados_ctx(store, s); // XXX holds std::map
+     /* XXX and -then- stash req_state pointers everywhere they are needed */
+     ret = req->init(rgw_env, &rados_ctx, io, s);
+     if (ret < 0) {
+       dout(10) << "failed to initialize request" << dendl;
+       abort_req(s, op, ret);
+       goto done;
+     }
+     /* req is-a RGWOp, currently initialized separately */
+     ret = req->op_init();
+     if (ret < 0) {
+       dout(10) << "failed to initialize RGWOp" << dendl;
+       abort_req(s, op, ret);
+       goto done;
+     }
+     /* XXX authorize does less here then in the REST path, e.g.,
+      * the user's info is cached, but still incomplete */
+     req->log(s, "authorizing");
+     ret = req->authorize();
+     if (ret < 0) {
+       dout(10) << "failed to authorize request" << dendl;
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "reading op permissions");
+     ret = req->read_permissions(op);
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "init op");
+     ret = op->init_processing();
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "verifying op mask");
+     ret = op->verify_op_mask();
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "verifying op permissions");
+     ret = op->verify_permission();
+     if (ret < 0) {
+       if (s->system_request) {
+       dout(2) << "overriding permissions due to system operation" << dendl;
+       } else {
+       abort_req(s, op, ret);
+       goto done;
+       }
+     }
+     req->log(s, "verifying op params");
+     ret = op->verify_params();
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "executing");
+     op->pre_exec();
+     op->execute();
+     op->complete();
+   done:
+     int r = io->complete_request();
+     if (r < 0) {
+       dout(0) << "ERROR: io->complete_request() returned " << r << dendl;
+     }
+     if (should_log) {
+       rgw_log_op(store, s, (op ? op->name() : "unknown"), olog);
+     }
+     int http_ret = s->err.http_ret;
+     req->log_format(s, "http status=%d", http_ret);
+     dout(1) << "====== " << __func__
+           << " req done req=" << hex << req << dec << " http_status="
+           << http_ret
+           << " ======" << dendl;
+     return (ret < 0 ? ret : s->err.ret);
+   } /* process_request */
+   int RGWLibProcess::start_request(RGWLibContinuedReq* req)
+   {
+     dout(1) << "====== " << __func__
+           << " starting new continued request req=" << hex << req << dec
+           << " ======" << dendl;
+     /*
+      * invariant: valid requests are derived from RGWOp--well-formed
+      * requests should have assigned RGWRequest::op in their descendant
+      * constructor--if not, the compiler can find it, at the cost of
+      * a runtime check
+      */
+     RGWOp *op = (req->op) ? req->op : dynamic_cast<RGWOp*>(req);
+     if (! op) {
+       dout(1) << "failed to derive cognate RGWOp (invalid op?)" << dendl;
+       return -EINVAL;
+     }
+     struct req_state* s = req->get_state();
+     /* req is-a RGWOp, currently initialized separately */
+     int ret = req->op_init();
+     if (ret < 0) {
+       dout(10) << "failed to initialize RGWOp" << dendl;
+       abort_req(s, op, ret);
+       goto done;
+     }
+     /* XXX authorize does less here then in the REST path, e.g.,
+      * the user's info is cached, but still incomplete */
+     req->log(s, "authorizing");
+     ret = req->authorize();
+     if (ret < 0) {
+       dout(10) << "failed to authorize request" << dendl;
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "reading op permissions");
+     ret = req->read_permissions(op);
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "init op");
+     ret = op->init_processing();
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "verifying op mask");
+     ret = op->verify_op_mask();
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
+     }
+     req->log(s, "verifying op permissions");
+     ret = op->verify_permission();
+     if (ret < 0) {
+       if (s->system_request) {
+       dout(2) << "overriding permissions due to system operation" << dendl;
+       } else {
+       abort_req(s, op, ret);
+       goto done;
+       }
+     }
+     req->log(s, "verifying op params");
+     ret = op->verify_params();
+     if (ret < 0) {
+       abort_req(s, op, ret);
+       goto done;
      }
-     bufferlist bl;
-     policy->encode(bl);
  
-     bin_ = (char*)malloc(bl.length());
-     if (!bin_) {
-       return -ENOBUFS;
+     op->pre_exec();
+     req->exec_start();
+   done:
+     return (ret < 0 ? ret : s->err.ret);
+   }
+   int RGWLibProcess::finish_request(RGWLibContinuedReq* req)
+   {
+     RGWOp *op = (req->op) ? req->op : dynamic_cast<RGWOp*>(req);
+     if (! op) {
+       dout(1) << "failed to derive cognate RGWOp (invalid op?)" << dendl;
+       return -EINVAL;
      }
-     int bin_len_ = bl.length();
-     bl.copy(0, bin_len_, bin_);
  
-     *bin = bin_;
-     *bin_len = bin_len_;
+     int ret = req->exec_finish();
+     int op_ret = op->get_ret();
+     dout(1) << "====== " << __func__
+           << " finishing continued request req=" << hex << req << dec
+           << " op status=" << op_ret
+           << " ======" << dendl;
+     return ret;
+   }
+   int RGWLibFrontend::init()
+   {
+     pprocess = new RGWLibProcess(g_ceph_context, &env,
+                                g_conf->rgw_thread_pool_size, conf);
      return 0;
    }
-   catch (const std::exception &e) {
-     lderr(rgw) << "librgw_acl_bin2xml: caught exception " << e.what() << dendl;
+   int RGWLib::init()
+   {
+     vector<const char*> args;
+     return init(args);
    }
-   catch (...) {
-     lderr(rgw) << "librgw_acl_bin2xml: caught unknown exception " << dendl;
+   int RGWLib::init(vector<const char*>& args)
+   {
+     int r = 0;
+     /* alternative default for module */
+     vector<const char *> def_args;
+     def_args.push_back("--debug-rgw=1/5");
+     def_args.push_back("--keyring=$rgw_data/keyring");
+     def_args.push_back("--log-file=/var/log/radosgw/$cluster-$name.log");
+     global_init(&def_args, args,
+               CEPH_ENTITY_TYPE_CLIENT,
+               CODE_ENVIRONMENT_DAEMON,
+               CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS);
+     Mutex mutex("main");
+     SafeTimer init_timer(g_ceph_context, mutex);
+     init_timer.init();
+     mutex.Lock();
+     init_timer.add_event_after(g_conf->rgw_init_timeout, new C_InitTimeout);
+     mutex.Unlock();
+     common_init_finish(g_ceph_context);
+     rgw_tools_init(g_ceph_context);
+     rgw_init_resolver();
+     store = RGWStoreManager::get_storage(g_ceph_context,
+                                        g_conf->rgw_enable_gc_threads,
 -                                       g_conf->rgw_enable_quota_threads);
++                                       g_conf->rgw_enable_quota_threads,
++                                       g_conf->rgw_run_sync_thread);
+     if (!store) {
+       mutex.Lock();
+       init_timer.cancel_all_events();
+       init_timer.shutdown();
+       mutex.Unlock();
+       derr << "Couldn't init storage provider (RADOS)" << dendl;
+       return -EIO;
+     }
+     r = rgw_perf_start(g_ceph_context);
 -    rgw_rest_init(g_ceph_context, store->region);
++    rgw_rest_init(g_ceph_context, store, store->get_zonegroup());
+     mutex.Lock();
+     init_timer.cancel_all_events();
+     init_timer.shutdown();
+     mutex.Unlock();
+     if (r)
+       return -EIO;
+     rgw_user_init(store);
+     rgw_bucket_init(store->meta_mgr);
+     rgw_log_usage_init(g_ceph_context, store);
+     // XXX ex-RGWRESTMgr_lib, mgr->set_logging(true)
+     if (!g_conf->rgw_ops_log_socket_path.empty()) {
+       olog = new OpsLogSocket(g_ceph_context, g_conf->rgw_ops_log_data_backlog);
+       olog->init(g_conf->rgw_ops_log_socket_path);
+     }
+     int port = 80;
+     RGWProcessEnv env = { store, &rest, olog, port };
+     fec = new RGWFrontendConfig("rgwlib");
+     fe = new RGWLibFrontend(env, fec);
+     fe->init();
+     if (r < 0) {
+       derr << "ERROR: failed initializing frontend" << dendl;
+       return r;
+     }
+     fe->run();
+     return 0;
+   } /* RGWLib::init() */
+   int RGWLib::stop()
+   {
+     derr << "shutting down" << dendl;
+     fe->stop();
+     fe->join();
+     delete fe;
+     rgw_log_usage_finalize();
+     delete olog;
+     RGWStoreManager::close_storage(store);
+     rgw_tools_cleanup();
+     rgw_shutdown_resolver();
+     rgw_perf_stop(g_ceph_context);
+     dout(1) << "final shutdown" << dendl;
+     g_ceph_context->put();
+     ceph::crypto::shutdown();
+     return 0;
+   } /* RGWLib::stop() */
+   int RGWLibIO::set_uid(RGWRados *store, const rgw_user& uid)
+   {
+     int ret = rgw_get_user_info_by_uid(store, uid, user_info, NULL);
+     if (ret < 0) {
+       derr << "ERROR: failed reading user info: uid=" << uid << " ret="
+          << ret << dendl;
+     }
+     return ret;
    }
-   if (!bin_)
-     free(bin_);
-   bin_ = NULL;
-   return -2000;
- }
  
- void librgw_free_bin(librgw_t rgw, char *bin)
+   int RGWLibRequest::read_permissions(RGWOp* op) {
+     int ret =
+       rgw_build_bucket_policies(rgwlib.get_store(), get_state());
+     if (ret < 0) {
+       ldout(get_state()->cct, 10) << "read_permissions on "
+                                 << get_state()->bucket << ":"
+                                 << get_state()->object
+                                 << " only_bucket=" << only_bucket()
+                                 << " ret=" << ret << dendl;
+       if (ret == -ENODATA)
+       ret = -EACCES;
+     }
+     return ret;
+   } /* RGWLibRequest::read_permissions */
+   int RGWHandler_Lib::authorize()
+   {
+     /* TODO: handle
+      *  1. subusers
+      *  2. anonymous access
+      *  3. system access
+      *  4. ?
+      *
+      *  Much or all of this depends on handling the cached authorization
+      *  correctly (e.g., dealing with keystone) at mount time.
+      */
+     s->perm_mask = RGW_PERM_FULL_CONTROL;
+     // populate the owner info
+     s->owner.set_id(s->user->user_id);
+     s->owner.set_name(s->user->display_name);
+     return 0;
+   } /* RGWHandler_Lib::authorize */
+ } /* namespace rgw */
+ extern "C" {
+ int librgw_create(librgw_t* rgw, int argc, char **argv)
  {
-   free(bin);
+   using namespace rgw;
+   int rc = -EINVAL;
+   if (! g_ceph_context) {
+     std::lock_guard<std::mutex> lg(librgw_mtx);
+     if (! g_ceph_context) {
+       vector<const char*> args;
+       argv_to_vec(argc, const_cast<const char**>(argv), args);
+       rc = rgwlib.init(args);
+     }
+   }
+   *rgw = g_ceph_context->get();
+   return rc;
  }
  
  void librgw_shutdown(librgw_t rgw)
index fb4c2a927b91cff9dbfedffa37f501202ea7f844,885aca66305194515b269badc3ae8278ae44f9ce..3d94720aedbaff511a318da6ea26442c49012d98
@@@ -47,193 -43,150 +47,194 @@@ static RGWRados *store = NULL
  
  void _usage() 
  {
-   cerr << "usage: radosgw-admin <cmd> [options...]" << std::endl;
-   cerr << "commands:\n";
-   cerr << "  user create                create a new user\n" ;
-   cerr << "  user modify                modify user\n";
-   cerr << "  user info                  get user info\n";
-   cerr << "  user rm                    remove user\n";
-   cerr << "  user suspend               suspend a user\n";
-   cerr << "  user enable                re-enable user after suspension\n";
-   cerr << "  user check                 check user info\n";
-   cerr << "  user stats                 show user stats as accounted by quota subsystem\n";
-   cerr << "  caps add                   add user capabilities\n";
-   cerr << "  caps rm                    remove user capabilities\n";
-   cerr << "  subuser create             create a new subuser\n" ;
-   cerr << "  subuser modify             modify subuser\n";
-   cerr << "  subuser rm                 remove subuser\n";
-   cerr << "  key create                 create access key\n";
-   cerr << "  key rm                     remove access key\n";
-   cerr << "  bucket list                list buckets\n";
-   cerr << "  bucket link                link bucket to specified user\n";
-   cerr << "  bucket unlink              unlink bucket from specified user\n";
-   cerr << "  bucket stats               returns bucket statistics\n";
-   cerr << "  bucket rm                  remove bucket\n";
-   cerr << "  bucket check               check bucket index\n";
-   cerr << "  object rm                  remove object\n";
-   cerr << "  object unlink              unlink object from bucket index\n";
-   cerr << "  objects expire             run expired objects cleanup\n";
-   cerr << "  period prepare             prepare a new period\n";
-   cerr << "  period delete              delete a period\n";
-   cerr << "  period get                 get period info\n";
-   cerr << "  period get-current         get current period info\n";
-   cerr << "  period pull                pull a period\n";
-   cerr << "  period push                push a period\n";
-   cerr << "  period list                list all periods\n";
-   cerr << "  period update              update the staging period\n";
-   cerr << "  period commit              commit the staging period\n";
-   cerr << "  quota set                  set quota params\n";
-   cerr << "  quota enable               enable quota\n";
-   cerr << "  quota disable              disable quota\n";
-   cerr << "  realm create               create a new realm\n";
-   cerr << "  realm delete               delete a realm\n";
-   cerr << "  realm get                  show realm info\n";
-   cerr << "  realm get-default          get default realm name\n";
-   cerr << "  realm list                 list realms\n";
-   cerr << "  realm list-periods         list all realm periods\n";
-   cerr << "  realm remove               remove a zonegroup from the realm\n";
-   cerr << "  realm rename               rename a realm\n";
-   cerr << "  realm set                  set realm info (requires infile)\n";
-   cerr << "  realm default              set realm as default\n";
-   cerr << "  realm pull                 pull a realm and its current period\n";
-   cerr << "  zonegroup add              add a zone to a zonegroup\n";
-   cerr << "  zonegroup create           create a new zone group info\n";
-   cerr << "  zonegroup default          set default zone group\n";
-   cerr << "  zonegroup delete           delete a zone group info\n";
-   cerr << "  zonegroup get              show zone group info\n";
-   cerr << "  zonegroup modify           set/clear zonegroup master status\n";
-   cerr << "  zonegroup set              set zone group info (requires infile)\n";
-   cerr << "  zonegroup rename           rename a zone group\n";
-   cerr << "  zonegroup list             list all zone groups set on this cluster\n";
-   cerr << "  zonegroup-map get          show zonegroup-map\n";
-   cerr << "  zonegroup-map set          set zonegroup-map (requires infile)\n";
-   cerr << "  zone create                create a new zone\n";
-   cerr << "  zone get                   show zone cluster params\n";
-   cerr << "  zone modify                set/clear zone master status\n";
-   cerr << "  zone set                   set zone cluster params (requires infile)\n";
-   cerr << "  zone list                  list all zones set on this cluster\n";
-   cerr << "  pool add                   add an existing pool for data placement\n";
-   cerr << "  pool rm                    remove an existing pool from data placement set\n";
-   cerr << "  pools list                 list placement active set\n";
-   cerr << "  policy                     read bucket/object policy\n";
-   cerr << "  log list                   list log objects\n";
-   cerr << "  log show                   dump a log from specific object or (bucket + date\n";
-   cerr << "                             + bucket-id)\n";
-   cerr << "                             (NOTE: required to specify formatting of date\n";
-   cerr << "                             to \"YYYY-MM-DD-hh\")\n";
-   cerr << "  log rm                     remove log object\n";
-   cerr << "  usage show                 show usage (by user, date range)\n";
-   cerr << "  usage trim                 trim usage (by user, date range)\n";
-   cerr << "  temp remove                remove temporary objects that were created up to\n";
-   cerr << "                             specified date (and optional time)\n";
-   cerr << "  gc list                    dump expired garbage collection objects (specify\n";
-   cerr << "                             --include-all to list all entries, including unexpired)\n";
-   cerr << "  gc process                 manually process garbage\n";
-   cerr << "  metadata get               get metadata info\n";
-   cerr << "  metadata put               put metadata info\n";
-   cerr << "  metadata rm                remove metadata info\n";
-   cerr << "  metadata list              list metadata info\n";
-   cerr << "  mdlog list                 list metadata log\n";
-   cerr << "  mdlog trim                 trim metadata log (use start-date, end-date or\n";
-   cerr << "                             start-marker, end-marker)\n";
-   cerr << "  mdlog status               read metadata log status\n";
-   cerr << "  bilog list                 list bucket index log\n";
-   cerr << "  bilog trim                 trim bucket index log (use start-marker, end-marker)\n";
-   cerr << "  datalog list               list data log\n";
-   cerr << "  datalog trim               trim data log\n";
-   cerr << "  datalog status             read data log status\n";
-   cerr << "  opstate list               list stateful operations entries (use client_id,\n";
-   cerr << "                             op_id, object)\n";
-   cerr << "  opstate set                set state on an entry (use client_id, op_id, object, state)\n";
-   cerr << "  opstate renew              renew state on an entry (use client_id, op_id, object)\n";
-   cerr << "  opstate rm                 remove entry (use client_id, op_id, object)\n";
-   cerr << "  replicalog get             get replica metadata log entry\n";
-   cerr << "  replicalog update          update replica metadata log entry\n";
-   cerr << "  replicalog delete          delete replica metadata log entry\n";
-   cerr << "  orphans find               init and run search for leaked rados objects\n";
-   cerr << "  orphans finish             clean up search for leaked rados objects\n";
-   cerr << "options:\n";
-   cerr << "   --uid=<id>                user id\n";
-   cerr << "   --subuser=<name>          subuser name\n";
-   cerr << "   --access-key=<key>        S3 access key\n";
-   cerr << "   --email=<email>\n";
-   cerr << "   --secret/--secret-key=<key>\n";
-   cerr << "                             specify secret key\n";
-   cerr << "   --gen-access-key          generate random access key (for S3)\n";
-   cerr << "   --gen-secret              generate random secret key\n";
-   cerr << "   --key-type=<type>         key type, options are: swift, s3\n";
-   cerr << "   --temp-url-key[-2]=<key>  temp url key\n";
-   cerr << "   --access=<access>         Set access permissions for sub-user, should be one\n";
-   cerr << "                             of read, write, readwrite, full\n";
-   cerr << "   --display-name=<name>\n";
-   cerr << "   --max_buckets             max number of buckets for a user\n";
-   cerr << "   --system                  set the system flag on the user\n";
-   cerr << "   --bucket=<bucket>\n";
-   cerr << "   --pool=<pool>\n";
-   cerr << "   --object=<object>\n";
-   cerr << "   --date=<date>\n";
-   cerr << "   --start-date=<date>\n";
-   cerr << "   --end-date=<date>\n";
-   cerr << "   --bucket-id=<bucket-id>\n";
-   cerr << "   --shard-id=<shard-id>     optional for mdlog list\n";
-   cerr << "                             required for: \n";
-   cerr << "                               mdlog trim\n";
-   cerr << "                               replica mdlog get/delete\n";
-   cerr << "                               replica datalog get/delete\n";
-   cerr << "   --metadata-key=<key>      key to retrieve metadata from with metadata get\n";
-   cerr << "   --remote=<remote>         remote to pull period\n";
-   cerr << "   --parent=<id>             parent period id\n";
-   cerr << "   --period=<id>             period id\n";
-   cerr << "   --epoch=<number>          period epoch\n";
-   cerr << "   --commit                  commit the period during 'period update'\n";
-   cerr << "   --master                  set as master\n";
-   cerr << "   --master-url              master url\n";
-   cerr << "   --master-zonegroup=<id>   master zonegroup id\n";
-   cerr << "   --master-zone=<id>        master zone id\n";
-   cerr << "   --rgw-realm=<realm>       realm name\n";
-   cerr << "   --realm-id=<realm id>     realm id\n";
-   cerr << "   --realm-new-name=<realm new name> realm new name\n";
-   cerr << "   --rgw-zonegroup=<zonegroup>   zonegroup name\n";
-   cerr << "   --rgw-zone=<zone>         zone in which radosgw is running\n";
-   cerr << "   --zone-new-name=<zone>    zone new name\n";
-   cerr << "   --default                 set entity (realm, zonegroup, zone) as default\n";
-   cerr << "   --endpoints=<list>        zone endpoints\n";
-   cerr << "   --fix                     besides checking bucket index, will also fix it\n";
-   cerr << "   --check-objects           bucket check: rebuilds bucket index according to\n";
-   cerr << "                             actual objects state\n";
-   cerr << "   --format=<format>         specify output format for certain operations: xml,\n";
-   cerr << "                             json\n";
-   cerr << "   --purge-data              when specified, user removal will also purge all the\n";
-   cerr << "                             user data\n";
-   cerr << "   --purge-keys              when specified, subuser removal will also purge all the\n";
-   cerr << "                             subuser keys\n";
-   cerr << "   --purge-objects           remove a bucket's objects before deleting it\n";
-   cerr << "                             (NOTE: required to delete a non-empty bucket)\n";
-   cerr << "   --sync-stats              option to 'user stats', update user stats with current\n";
-   cerr << "                             stats reported by user's buckets indexes\n";
-   cerr << "   --show-log-entries=<flag> enable/disable dump of log entries on log show\n";
-   cerr << "   --show-log-sum=<flag>     enable/disable dump of log summation on log show\n";
-   cerr << "   --skip-zero-entries       log show only dumps entries that don't have zero value\n";
-   cerr << "                             in one of the numeric field\n";
-   cerr << "   --infile                  specify a file to read in when setting data\n";
-   cerr << "   --state=<state string>    specify a state for the opstate set command\n";
-   cerr << "   --replica-log-type        replica log type (metadata, data, bucket), required for\n";
-   cerr << "                             replica log operations\n";
-   cerr << "   --categories=<list>       comma separated list of categories, used in usage show\n";
-   cerr << "   --caps=<caps>             list of caps (e.g., \"usage=read, write; user=read\"\n";
-   cerr << "   --yes-i-really-mean-it    required for certain operations\n";
-   cerr << "   --reset-regions           reset regionmap when regionmap update";
-   cerr << "\n";
-   cerr << "<date> := \"YYYY-MM-DD[ hh:mm:ss]\"\n";
-   cerr << "\nQuota options:\n";
-   cerr << "   --bucket                  specified bucket for quota command\n";
-   cerr << "   --max-objects             specify max objects (negative value to disable)\n";
-   cerr << "   --max-size                specify max size (in bytes, negative value to disable)\n";
-   cerr << "   --quota-scope             scope of quota (bucket, user)\n";
-   cerr << "\nOrphans search options:\n";
-   cerr << "   --pool                    data pool to scan for leaked rados objects in\n";
-   cerr << "   --num-shards              num of shards to use for keeping the temporary scan info\n";
-   cerr << "\n";
+   cout << "usage: radosgw-admin <cmd> [options...]" << std::endl;
+   cout << "commands:\n";
+   cout << "  user create                create a new user\n" ;
+   cout << "  user modify                modify user\n";
+   cout << "  user info                  get user info\n";
+   cout << "  user rm                    remove user\n";
+   cout << "  user suspend               suspend a user\n";
+   cout << "  user enable                re-enable user after suspension\n";
+   cout << "  user check                 check user info\n";
+   cout << "  user stats                 show user stats as accounted by quota subsystem\n";
+   cout << "  caps add                   add user capabilities\n";
+   cout << "  caps rm                    remove user capabilities\n";
+   cout << "  subuser create             create a new subuser\n" ;
+   cout << "  subuser modify             modify subuser\n";
+   cout << "  subuser rm                 remove subuser\n";
+   cout << "  key create                 create access key\n";
+   cout << "  key rm                     remove access key\n";
+   cout << "  bucket list                list buckets\n";
+   cout << "  bucket link                link bucket to specified user\n";
+   cout << "  bucket unlink              unlink bucket from specified user\n";
+   cout << "  bucket stats               returns bucket statistics\n";
+   cout << "  bucket rm                  remove bucket\n";
+   cout << "  bucket check               check bucket index\n";
+   cout << "  object rm                  remove object\n";
+   cout << "  object unlink              unlink object from bucket index\n";
+   cout << "  objects expire             run expired objects cleanup\n";
++  cout << "  period prepare             prepare a new period\n";
++  cout << "  period delete              delete a period\n";
++  cout << "  period get                 get period info\n";
++  cout << "  period get-current         get current period info\n";
++  cout << "  period pull                pull a period\n";
++  cout << "  period push                push a period\n";
++  cout << "  period list                list all periods\n";
++  cout << "  period update              update the staging period\n";
++  cout << "  period commit              commit the staging period\n";
+   cout << "  quota set                  set quota params\n";
+   cout << "  quota enable               enable quota\n";
+   cout << "  quota disable              disable quota\n";
 -  cout << "  region get                 show region info\n";
 -  cout << "  regions list               list all regions set on this cluster\n";
 -  cout << "  region set                 set region info (requires infile)\n";
 -  cout << "  region default             set default region\n";
 -  cout << "  region-map get             show region-map\n";
 -  cout << "  region-map set             set region-map (requires infile)\n";
++  cout << "  realm create               create a new realm\n";
++  cout << "  realm delete               delete a realm\n";
++  cout << "  realm get                  show realm info\n";
++  cout << "  realm get-default          get default realm name\n";
++  cout << "  realm list                 list realms\n";
++  cout << "  realm list-periods         list all realm periods\n";
++  cout << "  realm remove               remove a zonegroup from the realm\n";
++  cout << "  realm rename               rename a realm\n";
++  cout << "  realm set                  set realm info (requires infile)\n";
++  cout << "  realm default              set realm as default\n";
++  cout << "  realm pull                 pull a realm and its current period\n";
++  cout << "  zonegroup add              add a zone to a zonegroup\n";
++  cout << "  zonegroup create           create a new zone group info\n";
++  cout << "  zonegroup default          set default zone group\n";
++  cout << "  zonegroup delete           delete a zone group info\n";
++  cout << "  zonegroup get              show zone group info\n";
++  cout << "  zonegroup modify           set/clear zonegroup master status\n";
++  cout << "  zonegroup set              set zone group info (requires infile)\n";
++  cout << "  zonegroup rename           rename a zone group\n";
++  cout << "  zonegroup list             list all zone groups set on this cluster\n";
++  cout << "  zonegroup-map get          show zonegroup-map\n";
++  cout << "  zonegroup-map set          set zonegroup-map (requires infile)\n";
++  cout << "  zone create                create a new zone\n";
+   cout << "  zone get                   show zone cluster params\n";
++  cout << "  zone modify                set/clear zone master status\n";
+   cout << "  zone set                   set zone cluster params (requires infile)\n";
+   cout << "  zone list                  list all zones set on this cluster\n";
+   cout << "  pool add                   add an existing pool for data placement\n";
+   cout << "  pool rm                    remove an existing pool from data placement set\n";
+   cout << "  pools list                 list placement active set\n";
+   cout << "  policy                     read bucket/object policy\n";
+   cout << "  log list                   list log objects\n";
+   cout << "  log show                   dump a log from specific object or (bucket + date\n";
+   cout << "                             + bucket-id)\n";
+   cout << "                             (NOTE: required to specify formatting of date\n";
+   cout << "                             to \"YYYY-MM-DD-hh\")\n";
+   cout << "  log rm                     remove log object\n";
+   cout << "  usage show                 show usage (by user, date range)\n";
+   cout << "  usage trim                 trim usage (by user, date range)\n";
+   cout << "  temp remove                remove temporary objects that were created up to\n";
+   cout << "                             specified date (and optional time)\n";
+   cout << "  gc list                    dump expired garbage collection objects (specify\n";
+   cout << "                             --include-all to list all entries, including unexpired)\n";
+   cout << "  gc process                 manually process garbage\n";
+   cout << "  metadata get               get metadata info\n";
+   cout << "  metadata put               put metadata info\n";
+   cout << "  metadata rm                remove metadata info\n";
+   cout << "  metadata list              list metadata info\n";
+   cout << "  mdlog list                 list metadata log\n";
+   cout << "  mdlog trim                 trim metadata log (use start-date, end-date or\n";
+   cout << "                             start-marker, end-marker)\n";
++  cout << "  mdlog status               read metadata log status\n";
+   cout << "  bilog list                 list bucket index log\n";
+   cout << "  bilog trim                 trim bucket index log (use start-marker, end-marker)\n";
+   cout << "  datalog list               list data log\n";
+   cout << "  datalog trim               trim data log\n";
++  cout << "  datalog status             read data log status\n";
+   cout << "  opstate list               list stateful operations entries (use client_id,\n";
+   cout << "                             op_id, object)\n";
+   cout << "  opstate set                set state on an entry (use client_id, op_id, object, state)\n";
+   cout << "  opstate renew              renew state on an entry (use client_id, op_id, object)\n";
+   cout << "  opstate rm                 remove entry (use client_id, op_id, object)\n";
+   cout << "  replicalog get             get replica metadata log entry\n";
+   cout << "  replicalog update          update replica metadata log entry\n";
+   cout << "  replicalog delete          delete replica metadata log entry\n";
+   cout << "  orphans find               init and run search for leaked rados objects\n";
+   cout << "  orphans finish             clean up search for leaked rados objects\n";
+   cout << "options:\n";
+   cout << "   --tenant=<tenant>         tenant name\n";
+   cout << "   --uid=<id>                user id\n";
+   cout << "   --subuser=<name>          subuser name\n";
+   cout << "   --access-key=<key>        S3 access key\n";
+   cout << "   --email=<email>\n";
+   cout << "   --secret/--secret-key=<key>\n";
+   cout << "                             specify secret key\n";
+   cout << "   --gen-access-key          generate random access key (for S3)\n";
+   cout << "   --gen-secret              generate random secret key\n";
+   cout << "   --key-type=<type>         key type, options are: swift, s3\n";
+   cout << "   --temp-url-key[-2]=<key>  temp url key\n";
+   cout << "   --access=<access>         Set access permissions for sub-user, should be one\n";
+   cout << "                             of read, write, readwrite, full\n";
+   cout << "   --display-name=<name>\n";
+   cout << "   --max_buckets             max number of buckets for a user\n";
+   cout << "   --system                  set the system flag on the user\n";
+   cout << "   --bucket=<bucket>\n";
+   cout << "   --pool=<pool>\n";
+   cout << "   --object=<object>\n";
+   cout << "   --date=<date>\n";
+   cout << "   --start-date=<date>\n";
+   cout << "   --end-date=<date>\n";
+   cout << "   --bucket-id=<bucket-id>\n";
+   cout << "   --shard-id=<shard-id>     optional for mdlog list\n";
+   cout << "                             required for: \n";
+   cout << "                               mdlog trim\n";
+   cout << "                               replica mdlog get/delete\n";
+   cout << "                               replica datalog get/delete\n";
+   cout << "   --metadata-key=<key>      key to retrieve metadata from with metadata get\n";
 -  cout << "   --rgw-region=<region>     region in which radosgw is running\n";
++  cout << "   --remote=<remote>         remote to pull period\n";
++  cout << "   --parent=<id>             parent period id\n";
++  cout << "   --period=<id>             period id\n";
++  cout << "   --epoch=<number>          period epoch\n";
++  cout << "   --commit                  commit the period during 'period update'\n";
++  cout << "   --master                  set as master\n";
++  cout << "   --master-url              master url\n";
++  cout << "   --master-zonegroup=<id>   master zonegroup id\n";
++  cout << "   --master-zone=<id>        master zone id\n";
++  cout << "   --rgw-realm=<realm>       realm name\n";
++  cout << "   --realm-id=<realm id>     realm id\n";
++  cout << "   --realm-new-name=<realm new name> realm new name\n";
++  cout << "   --rgw-zonegroup=<zonegroup>   zonegroup name\n";
+   cout << "   --rgw-zone=<zone>         zone in which radosgw is running\n";
++  cout << "   --zone-new-name=<zone>    zone new name\n";
++  cout << "   --default                 set entity (realm, zonegroup, zone) as default\n";
++  cout << "   --endpoints=<list>        zone endpoints\n";
+   cout << "   --fix                     besides checking bucket index, will also fix it\n";
+   cout << "   --check-objects           bucket check: rebuilds bucket index according to\n";
+   cout << "                             actual objects state\n";
+   cout << "   --format=<format>         specify output format for certain operations: xml,\n";
+   cout << "                             json\n";
+   cout << "   --purge-data              when specified, user removal will also purge all the\n";
+   cout << "                             user data\n";
+   cout << "   --purge-keys              when specified, subuser removal will also purge all the\n";
+   cout << "                             subuser keys\n";
+   cout << "   --purge-objects           remove a bucket's objects before deleting it\n";
+   cout << "                             (NOTE: required to delete a non-empty bucket)\n";
+   cout << "   --sync-stats              option to 'user stats', update user stats with current\n";
+   cout << "                             stats reported by user's buckets indexes\n";
+   cout << "   --show-log-entries=<flag> enable/disable dump of log entries on log show\n";
+   cout << "   --show-log-sum=<flag>     enable/disable dump of log summation on log show\n";
+   cout << "   --skip-zero-entries       log show only dumps entries that don't have zero value\n";
+   cout << "                             in one of the numeric field\n";
+   cout << "   --infile=<file>           specify a file to read in when setting data\n";
+   cout << "   --state=<state string>    specify a state for the opstate set command\n";
+   cout << "   --replica-log-type        replica log type (metadata, data, bucket), required for\n";
+   cout << "                             replica log operations\n";
+   cout << "   --categories=<list>       comma separated list of categories, used in usage show\n";
+   cout << "   --caps=<caps>             list of caps (e.g., \"usage=read, write; user=read\"\n";
+   cout << "   --yes-i-really-mean-it    required for certain operations\n";
+   cout << "   --reset-regions           reset regionmap when regionmap update";
+   cout << "\n";
+   cout << "<date> := \"YYYY-MM-DD[ hh:mm:ss]\"\n";
+   cout << "\nQuota options:\n";
+   cout << "   --bucket                  specified bucket for quota command\n";
+   cout << "   --max-objects             specify max objects (negative value to disable)\n";
+   cout << "   --max-size                specify max size (in bytes, negative value to disable)\n";
+   cout << "   --quota-scope             scope of quota (bucket, user)\n";
+   cout << "\nOrphans search options:\n";
+   cout << "   --pool                    data pool to scan for leaked rados objects in\n";
+   cout << "   --num-shards              num of shards to use for keeping the temporary scan info\n";
+   cout << "\n";
    generic_client_usage();
  }
  
Simple merge
Simple merge
index 0000000000000000000000000000000000000000,cb3201401578a3ff5cef417d6fa5befded1e14c5..e5c1b18d0cfac96c6555723db0b3cb680560b6d4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,67 +1,70 @@@
 -  RGWProcessEnv* pe = static_cast<RGWProcessEnv *>(req_info->user_data);
+ // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+ // vim: ts=8 sw=2 smarttab
+ #include "rgw_frontend.h"
+ #define dout_subsys ceph_subsys_rgw
+ static int civetweb_callback(struct mg_connection* conn) {
+   struct mg_request_info* req_info = mg_get_request_info(conn);
 -  RGWRequest* req = new RGWRequest(store->get_new_req_id());
++  RGWMongooseEnv* pe = static_cast<RGWMongooseEnv *>(req_info->user_data);
+   RGWRados* store = pe->store;
+   RGWREST* rest = pe->rest;
+   OpsLogSocket* olog = pe->olog;
 -  int ret = process_request(store, rest, req, &client_io, olog);
 -  if (ret < 0) {
 -    /* we don't really care about return code */
 -    dout(20) << "process_request() returned " << ret << dendl;
 -  }
++  RGWRequest req(store->get_new_req_id());
+   RGWMongoose client_io(conn, pe->port);
 -  delete req;
++  {
++    // hold a read lock over access to pe->store for reconfiguration
++    RWLock::RLocker lock(pe->mutex);
++    int ret = process_request(pe->store, rest, &req, &client_io, olog);
++    if (ret < 0) {
++      /* we don't really care about return code */
++      dout(20) << "process_request() returned " << ret << dendl;
++    }
++  }
+ // Mark as processed
+   return 1;
+ }
+ int RGWMongooseFrontend::run() {
+   char thread_pool_buf[32];
+   snprintf(thread_pool_buf, sizeof(thread_pool_buf), "%d",
+          (int)g_conf->rgw_thread_pool_size);
+   string port_str;
+   map<string, string> conf_map = conf->get_config_map();
+   conf->get_val("port", "80", &port_str);
+   conf_map.erase("port");
+   conf_map["listening_ports"] = port_str;
+   set_conf_default(conf_map, "enable_keep_alive", "yes");
+   set_conf_default(conf_map, "num_threads", thread_pool_buf);
+   set_conf_default(conf_map, "decode_url", "no");
+   const char *options[conf_map.size() * 2 + 1];
+   int i = 0;
+   for (map<string, string>::iterator iter = conf_map.begin();
+        iter != conf_map.end(); ++iter) {
+     options[i] = iter->first.c_str();
+     options[i + 1] = iter->second.c_str();
+     dout(20)<< "civetweb config: " << options[i] << ": "
+           << (options[i + 1] ? options[i + 1] : "<null>") << dendl;
+     i += 2;
+   }
+   options[i] = NULL;
+   struct mg_callbacks cb;
+   memset((void *)&cb, 0, sizeof(cb));
+   cb.begin_request = civetweb_callback;
+   cb.log_message = rgw_civetweb_log_callback;
+   cb.log_access = rgw_civetweb_log_access_callback;
+   ctx = mg_start(&cb, &env, (const char **)&options);
+   if (!ctx) {
+     return -EIO;
+   }
+   return 0;
+ } /* RGWMongooseFrontend::run */
Simple merge
index cb164ef29da051335ed241f57cbe2c8aa6c6b7f4,628fd26b4ed8a9479959fa8f7e1ad14c1c67669e..3cc3c4b64c979e4f3031cfc1fb8eddc375f7136e
@@@ -292,10 -290,8 +293,9 @@@ class RGWHTTPArg
    }
    /** parse the received arguments */
    int parse();
 +  void append(const string& name, const string& val);
    /** Get the value for a specific argument parameter */
    string& get(const string& name, bool *exists = NULL);
-   string& get(const char *name, bool *exists = NULL);
    int get_bool(const string& name, bool *val, bool *exists);
    int get_bool(const char *name, bool *val, bool *exists);
    void get_bool(const char *name, bool *val, bool def_val);
@@@ -1074,88 -1081,87 +1087,89 @@@ struct req_init_state 
    string src_bucket;
  };
  
+ /* XXX why don't RGWRequest (or descendants) hold this state? */
+ class RGWRequest;
  /** Store all the state necessary to complete and respond to an HTTP request*/
  struct req_state {
-    CephContext *cct;
-    RGWClientIO *cio;
-    http_op op;
-    bool content_started;
-    int format;
-    ceph::Formatter *formatter;
-    string decoded_uri;
-    string relative_uri;
-    const char *length;
-    int64_t content_length;
-    map<string, string> generic_attrs;
-    struct rgw_err err;
-    bool expect_cont;
-    bool header_ended;
-    uint64_t obj_size;
-    bool enable_ops_log;
-    bool enable_usage_log;
-    uint8_t defer_to_bucket_acls;
-    uint32_t perm_mask;
-    utime_t header_time;
-    /* Set once when url_bucket is parsed and not violated thereafter. */
-    string bucket_tenant;
-    string bucket_name;
-    rgw_bucket bucket;
-    rgw_obj_key object;
-    string src_tenant_name;
-    string src_bucket_name;
-    rgw_obj_key src_object;
-    ACLOwner bucket_owner;
-    ACLOwner owner;
-    string zonegroup_name;
-    string zonegroup_endpoint;
-    string bucket_instance_id;
-    int bucket_instance_shard_id;
-    string redirect;
-    RGWBucketInfo bucket_info;
-    map<string, bufferlist> bucket_attrs;
-    bool bucket_exists;
-    bool has_bad_meta;
-    RGWUserInfo user; 
-    RGWAccessControlPolicy *bucket_acl;
-    RGWAccessControlPolicy *object_acl;
-    bool system_request;
-    string canned_acl;
-    bool has_acl_header;
-    const char *http_auth;
-    bool local_source; /* source is local */
-    int prot_flags;
-    const char *os_auth_token;
-    string swift_user;
-    string swift_groups;
-    utime_t time;
-    void *obj_ctx;
-    string dialect;
-    string req_id;
-    string trans_id;
-    string host_id;
-    req_info info;
-    req_init_state init_state;
-    req_state(CephContext *_cct, class RGWEnv *e);
-    ~req_state();
+   CephContext *cct;
+   RGWClientIO *cio;
+   RGWRequest *req; /// XXX: re-remove??
+   http_op op;
+   bool content_started;
+   int format;
+   ceph::Formatter *formatter;
+   string decoded_uri;
+   string relative_uri;
+   const char *length;
+   int64_t content_length;
+   map<string, string> generic_attrs;
+   struct rgw_err err;
+   bool expect_cont;
+   bool header_ended;
+   uint64_t obj_size;
+   bool enable_ops_log;
+   bool enable_usage_log;
+   uint8_t defer_to_bucket_acls;
+   uint32_t perm_mask;
+   utime_t header_time;
+   /* Set once when url_bucket is parsed and not violated thereafter. */
+   string bucket_tenant;
+   string bucket_name;
+   rgw_bucket bucket;
+   rgw_obj_key object;
+   string src_tenant_name;
+   string src_bucket_name;
+   rgw_obj_key src_object;
+   ACLOwner bucket_owner;
+   ACLOwner owner;
 -  string region_endpoint;
++  string zonegroup_name;
++  string zonegroup_endpoint;
+   string bucket_instance_id;
++  int bucket_instance_shard_id;
+   string redirect;
+   RGWBucketInfo bucket_info;
+   map<string, bufferlist> bucket_attrs;
+   bool bucket_exists;
+   bool has_bad_meta;
+   RGWUserInfo *user;
+   RGWAccessControlPolicy *bucket_acl;
+   RGWAccessControlPolicy *object_acl;
+   bool system_request;
+   string canned_acl;
+   bool has_acl_header;
+   const char *http_auth;
+   bool local_source; /* source is local */
+   int prot_flags;
+   const char *os_auth_token;
+   string swift_user;
+   string swift_groups;
+   string host_id;
+   req_info info;
+   req_init_state init_state;
+   utime_t time;
+   void *obj_ctx;
+   string dialect;
+   string req_id;
+   string trans_id;
+   req_state(CephContext* _cct, RGWEnv* e, RGWUserInfo* u);
+   ~req_state();
  };
  
  /** Store basic data on an object */
index 0000000000000000000000000000000000000000,11497d4b0d0b45143b3b5c120fef4e9e3ac83132..928a03b8fe3a9b84850a0f4109d2aef34531907a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,167 +1,224 @@@
 -  RGWProcessEnv env;
+ // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+ // vim: ts=8 sw=2 smarttab
+ #ifndef RGW_FRONTEND_H
+ #define RGW_FRONTEND_H
+ #include "rgw_request.h"
+ #include "rgw_process.h"
++#include "rgw_realm_reloader.h"
+ #include "rgw_civetweb.h"
+ #include "rgw_civetweb_log.h"
+ #include "civetweb/civetweb.h"
+ #define dout_subsys ceph_subsys_rgw
+ class RGWFrontendConfig {
+   string config;
+   map<string, string> config_map;
+   int parse_config(const string& config, map<string, string>& config_map);
+   string framework;
+ public:
+   RGWFrontendConfig(const string& _conf) : config(_conf) {}
+   int init() {
+     int ret = parse_config(config, config_map);
+     if (ret < 0)
+       return ret;
+     return 0;
+   }
+   bool get_val(const string& key, const string& def_val, string *out);
+   bool get_val(const string& key, int def_val, int *out);
+   map<string, string>& get_config_map() { return config_map; }
+   string get_framework() { return framework; }
+ };
+ class RGWFrontend {
+ public:
+   virtual ~RGWFrontend() {}
+   virtual int init() = 0;
+   virtual int run() = 0;
+   virtual void stop() = 0;
+   virtual void join() = 0;
++
++  virtual void pause_for_new_config() = 0;
++  virtual void unpause_with_new_config(RGWRados *store) = 0;
++};
++
++struct RGWMongooseEnv : public RGWProcessEnv {
++  // every request holds a read lock, so we need to prioritize write locks to
++  // avoid starving pause_for_new_config()
++  static constexpr bool prioritize_write = true;
++  RWLock mutex;
++  RGWMongooseEnv(const RGWProcessEnv &env)
++    : RGWProcessEnv(env),
++      mutex("RGWMongooseFrontend", false, true, prioritize_write) {}
+ };
+ class RGWMongooseFrontend : public RGWFrontend {
+   RGWFrontendConfig* conf;
+   struct mg_context* ctx;
++  RGWMongooseEnv env;
+   void set_conf_default(map<string, string>& m, const string& key,
+                       const string& def_val) {
+     if (m.find(key) == m.end()) {
+       m[key] = def_val;
+     }
+   }
+ public:
+   RGWMongooseFrontend(RGWProcessEnv& pe, RGWFrontendConfig* _conf)
+     : conf(_conf), ctx(nullptr), env(pe) {
+   }
+   int init() {
+     return 0;
+   }
+   int run();
+   void stop() {
+     if (ctx) {
+       mg_stop(ctx);
+     }
+   }
+   void join() {
+   }
++
++  void pause_for_new_config() override {
++    // block callbacks until unpause
++    env.mutex.get_write();
++  }
++
++  void unpause_with_new_config(RGWRados *store) override {
++    env.store = store;
++    // unpause callbacks
++    env.mutex.put_write();
++  }
+ }; /* RGWMongooseFrontend */
+ class RGWProcessFrontend : public RGWFrontend {
+ protected:
+   RGWFrontendConfig* conf;
+   RGWProcess* pprocess;
+   RGWProcessEnv env;
+   RGWProcessControlThread* thread;
+ public:
+   RGWProcessFrontend(RGWProcessEnv& pe, RGWFrontendConfig* _conf)
+     : conf(_conf), pprocess(nullptr), env(pe), thread(nullptr) {
+   }
+   ~RGWProcessFrontend() {
+     delete thread;
+     delete pprocess;
+   }
+   int run() {
+     assert(pprocess); /* should have initialized by init() */
+     thread = new RGWProcessControlThread(pprocess);
+     thread->create("rgw_frontend");
+     return 0;
+   }
+   void stop();
+   void join() {
+     thread->join();
+   }
++
++  void pause_for_new_config() override {
++    pprocess->pause();
++  }
++
++  void unpause_with_new_config(RGWRados *store) override {
++    env.store = store;
++    pprocess->unpause_with_new_config(store);
++  }
+ }; /* RGWProcessFrontend */
+ class RGWFCGXFrontend : public RGWProcessFrontend {
+ public:
+   RGWFCGXFrontend(RGWProcessEnv& pe, RGWFrontendConfig* _conf)
+     : RGWProcessFrontend(pe, _conf) {}
+   int init() {
+     pprocess = new RGWFCGXProcess(g_ceph_context, &env,
+                                 g_conf->rgw_thread_pool_size, conf);
+     return 0;
+   }
+ }; /* RGWFCGXFrontend */
+ class RGWLoadGenFrontend : public RGWProcessFrontend {
+ public:
+   RGWLoadGenFrontend(RGWProcessEnv& pe, RGWFrontendConfig *_conf)
+     : RGWProcessFrontend(pe, _conf) {}
+   int init() {
+     int num_threads;
+     conf->get_val("num_threads", g_conf->rgw_thread_pool_size, &num_threads);
+     RGWLoadGenProcess *pp = new RGWLoadGenProcess(g_ceph_context, &env,
+                                                 num_threads, conf);
+     pprocess = pp;
+     string uid_str;
+     conf->get_val("uid", "", &uid_str);
+     if (uid_str.empty()) {
+       derr << "ERROR: uid param must be specified for loadgen frontend"
+          << dendl;
+       return EINVAL;
+     }
+     rgw_user uid(uid_str);
+     RGWUserInfo user_info;
+     int ret = rgw_get_user_info_by_uid(env.store, uid, user_info, NULL);
+     if (ret < 0) {
+       derr << "ERROR: failed reading user info: uid=" << uid << " ret="
+          << ret << dendl;
+       return ret;
+     }
+     map<string, RGWAccessKey>::iterator aiter = user_info.access_keys.begin();
+     if (aiter == user_info.access_keys.end()) {
+       derr << "ERROR: user has no S3 access keys set" << dendl;
+       return -EINVAL;
+     }
+     pp->set_access_key(aiter->second);
+     return 0;
+   }
+ }; /* RGWLoadGenFrontend */
++// FrontendPauser implementation for RGWRealmReloader
++class RGWFrontendPauser : public RGWRealmReloader::Pauser {
++  std::list<RGWFrontend*> &frontends;
++  RGWRealmReloader::Pauser* pauser;
++ public:
++  RGWFrontendPauser(std::list<RGWFrontend*> &frontends,
++                    RGWRealmReloader::Pauser* pauser = nullptr)
++    : frontends(frontends), pauser(pauser) {}
++
++  void pause() override {
++    for (auto frontend : frontends)
++      frontend->pause_for_new_config();
++    if (pauser)
++      pauser->pause();
++  }
++  void resume(RGWRados *store) {
++    for (auto frontend : frontends)
++      frontend->unpause_with_new_config(store);
++    if (pauser)
++      pauser->resume(store);
++  }
++};
++
+ #endif /* RGW_FRONTEND_H */
index 0000000000000000000000000000000000000000,9cea8e40242bfe80bc897b6a0bc11260770b6110..bd59f6072e520e8c6bc495d794fc9e29515678c1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,197 +1,199 @@@
+ // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+ // vim: ts=8 sw=2 smarttab
+ #ifndef RGW_LIB_H
+ #define RGW_LIB_H
+ #include <mutex>
+ #include "include/unordered_map.h"
+ #include "rgw_common.h"
+ #include "rgw_client_io.h"
+ #include "rgw_rest.h"
+ #include "rgw_request.h"
+ #include "rgw_frontend.h"
+ #include "rgw_process.h"
+ #include "rgw_rest_s3.h" // RGW_Auth_S3
+ class OpsLogSocket;
+ namespace rgw {
+   class RGWLibFrontendConfig;
+   class RGWLibFrontend;
+   class RGWLib {
+     RGWFrontendConfig* fec;
+     RGWLibFrontend* fe;
+     OpsLogSocket* olog;
+     RGWREST rest; // XXX needed for RGWProcessEnv
+     RGWProcessEnv env;
+     RGWRados* store;
+   public:
+     RGWLib() : fec(nullptr), fe(nullptr), olog(nullptr), store(nullptr)
+       {}
+     ~RGWLib() {}
+     RGWRados* get_store() { return store; }
+     RGWLibFrontend* get_fe() { return fe; }
+     int init();
+     int init(vector<const char *>& args);
+     int stop();
+   };
+   extern RGWLib rgwlib;
+ /* request interface */
+   class RGWLibIO : public RGWClientIO
+   {
+     RGWUserInfo user_info;
+   public:
+     RGWLibIO() {
+       get_env().set("HTTP_HOST", "");
+     }
+     RGWLibIO(const RGWUserInfo &_user_info)
+       : user_info(_user_info) {}
+     virtual void init_env(CephContext *cct) {}
+     const RGWUserInfo& get_user() {
+       return user_info;
+     }
+     int set_uid(RGWRados* store, const rgw_user& uid);
+     int write_data(const char *buf, int len);
+     int read_data(char *buf, int len);
+     int send_status(int status, const char *status_name);
+     int send_100_continue();
+     int complete_header();
+     int send_content_length(uint64_t len);
+     int complete_request() { /* XXX */
+       return 0;
+     };
+   }; /* RGWLibIO */
+ /* XXX */
+   class RGWRESTMgr_Lib : public RGWRESTMgr {
+   public:
+     RGWRESTMgr_Lib() {}
+     virtual ~RGWRESTMgr_Lib() {}
+   }; /* RGWRESTMgr_Lib */
+ /* XXX */
+   class RGWHandler_Lib : public RGWHandler {
+     friend class RGWRESTMgr_Lib;
+   public:
+     virtual int authorize();
+     RGWHandler_Lib() {}
+     virtual ~RGWHandler_Lib() {}
+     static int init_from_header(struct req_state *s);
+   }; /* RGWHandler_Lib */
+   class RGWLibRequest : public RGWRequest,
+                       public RGWHandler_Lib {
+   public:
+     CephContext* cct;
+     RGWUserInfo* user;
+     /* unambiguiously return req_state */
+     inline struct req_state* get_state() { return this->RGWRequest::s; }
+     RGWLibRequest(CephContext* _cct, RGWUserInfo* _user)
+       :  RGWRequest(0), cct(_cct), user(_user)
+       {}
+     RGWUserInfo* get_user() { return user; }
+   virtual int postauth_init() { return 0; }
+     /* descendant equivalent of *REST*::init_from_header(...):
+      * prepare request for execute()--should mean, fixup URI-alikes
+      * and any other expected stat vars in local req_state, for
+      * now */
+     virtual int header_init() = 0;
+     /* descendant initializer responsible to call RGWOp::init()--which
+      * descendants are required to inherit */
+     virtual int op_init() = 0;
++    using RGWHandler::init;
++
+     int init(const RGWEnv& rgw_env, RGWObjectCtx* rados_ctx,
+            RGWLibIO* io, struct req_state* _s) {
+       RGWRequest::init_state(_s);
+       RGWHandler::init(rados_ctx->store, _s, io);
+       /* fixup _s->req */
+       _s->req = this;
+       log_init();
+       get_state()->obj_ctx = rados_ctx;
+       get_state()->req_id = store->unique_id(id);
+       get_state()->trans_id = store->unique_trans_id(id);
+       log_format(_s, "initializing for trans_id = %s",
+                get_state()->trans_id.c_str());
+       int ret = header_init();
+       if (ret == 0) {
+       ret = init_from_header(_s);
+       }
+       return ret;
+     }
+     virtual bool only_bucket() = 0;
+     virtual int read_permissions(RGWOp *op);
+   }; /* RGWLibRequest */
+   class RGWLibContinuedReq : public RGWLibRequest {
+     RGWLibIO io_ctx;
+     struct req_state rstate;
+     RGWObjectCtx rados_ctx;
+   public:
+     RGWLibContinuedReq(CephContext* _cct, RGWUserInfo* _user)
+       :  RGWLibRequest(_cct, _user), io_ctx(),
+        rstate(_cct, &io_ctx.get_env(), _user), rados_ctx(rgwlib.get_store(),
+                                                          &rstate)
+       {
+       io_ctx.init(_cct);
+       RGWRequest::init_state(&rstate);
+       RGWHandler::init(rados_ctx.store, &rstate, &io_ctx);
+       /* fixup _s->req */
+       get_state()->req = this;
+       log_init();
+       get_state()->obj_ctx = &rados_ctx;
+       get_state()->req_id = store->unique_id(id);
+       get_state()->trans_id = store->unique_trans_id(id);
+       log_format(get_state(), "initializing for trans_id = %s",
+                  get_state()->trans_id.c_str());
+       }
+     inline RGWRados* get_store() { return store; }
+     virtual int execute() final { abort(); }
+     virtual int exec_start() = 0;
+     virtual int exec_continue() = 0;
+     virtual int exec_finish() = 0;
+   }; /* RGWLibContinuedReq */
+ } /* namespace rgw */
+ #endif /* RGW_LIB_H */
Simple merge
index d6bbaf9bd22c90bd67a3bec0ec31e98c29b23bf1,6a2c59e23a50aa819dd587a9a7d2f44a5da641a7..8f44299db0586e168c61aa7a5bc602b97f9f08d2
  
  #include <curl/curl.h>
  
 +#include <boost/intrusive_ptr.hpp>
 +
  #include "acconfig.h"
- #ifdef FASTCGI_INCLUDE_DIR
- # include "fastcgi/fcgiapp.h"
- #else
- # include "fcgiapp.h"
- #endif
- #include "rgw_fcgi.h"
  
  #include "common/ceph_argparse.h"
  #include "global/global_init.h"
  #include "include/str_list.h"
  #include "rgw_common.h"
  #include "rgw_rados.h"
- #include "rgw_acl.h"
  #include "rgw_user.h"
- #include "rgw_op.h"
 +#include "rgw_period_pusher.h"
 +#include "rgw_realm_reloader.h"
  #include "rgw_rest.h"
  #include "rgw_rest_s3.h"
  #include "rgw_rest_swift.h"
@@@ -842,293 -169,6 +174,9 @@@ static RGWRESTMgr *set_logging(RGWRESTM
    return mgr;
  }
  
- int RGWFrontendConfig::parse_config(const string& config, map<string, string>& config_map)
- {
-   list<string> config_list;
-   get_str_list(config, " ", config_list);
-   list<string>::iterator iter;
-   for (iter = config_list.begin(); iter != config_list.end(); ++iter) {
-     string& entry = *iter;
-     string key;
-     string val;
-     if (framework.empty()) {
-       framework = entry;
-       dout(0) << "framework: " << framework << dendl;
-       continue;
-     }
-     ssize_t pos = entry.find('=');
-     if (pos < 0) {
-       dout(0) << "framework conf key: " << entry << dendl;
-       config_map[entry] = "";
-       continue;
-     }
-     int ret = parse_key_value(entry, key, val);
-     if (ret < 0) {
-       cerr << "ERROR: can't parse " << entry << std::endl;
-       return ret;
-     }
-     dout(0) << "framework conf key: " << key << ", val: " << val << dendl;
-     config_map[key] = val;
-   }
-   return 0;
- }
- bool RGWFrontendConfig::get_val(const string& key, const string& def_val, string *out)
- {
-  map<string, string>::iterator iter = config_map.find(key);
-  if (iter == config_map.end()) {
-    *out = def_val;
-    return false;
-  }
-  *out = iter->second;
-  return true;
- }
- bool RGWFrontendConfig::get_val(const string& key, int def_val, int *out)
- {
-   string str;
-   bool found = get_val(key, "", &str);
-   if (!found) {
-     *out = def_val;
-     return false;
-   }
-   string err;
-   *out = strict_strtol(str.c_str(), 10, &err);
-   if (!err.empty()) {
-     cerr << "error parsing int: " << str << ": " << err << std::endl;
-     return -EINVAL;
-   }
-   return 0;
- }
- class RGWFrontend {
- public:
-   virtual ~RGWFrontend() {}
-   virtual int init() = 0;
-   virtual int run() = 0;
-   virtual void stop() = 0;
-   virtual void join() = 0;
-   virtual void pause_for_new_config() = 0;
-   virtual void unpause_with_new_config(RGWRados *store) = 0;
- };
- class RGWProcessControlThread : public Thread {
-   RGWProcess *pprocess;
- public:
-   explicit RGWProcessControlThread(RGWProcess *_pprocess) : pprocess(_pprocess) {}
-   void *entry() {
-     pprocess->run();
-     return NULL;
-   }
- };
- class RGWProcessFrontend : public RGWFrontend {
- protected:
-   RGWFrontendConfig *conf;
-   RGWProcess *pprocess;
-   RGWProcessEnv env;
-   RGWProcessControlThread *thread;
- public:
-   RGWProcessFrontend(RGWProcessEnv& pe, RGWFrontendConfig *_conf) : conf(_conf), pprocess(NULL), env(pe), thread(NULL) {
-   }
-   ~RGWProcessFrontend() {
-     delete thread;
-     delete pprocess;
-   }
-   int run() {
-     assert(pprocess); /* should have initialized by init() */
-     thread = new RGWProcessControlThread(pprocess);
-     thread->create("rgw_frontend");
-     return 0;
-   }
-   void stop() {
-     pprocess->close_fd();
-     thread->kill(SIGUSR1);
-   }
-   void join() {
-     thread->join();
-   }
-   void pause_for_new_config() override {
-     pprocess->pause();
-   }
-   void unpause_with_new_config(RGWRados *store) override {
-     env.store = store;
-     pprocess->unpause_with_new_config(store);
-   }
- };
- class RGWFCGXFrontend : public RGWProcessFrontend {
- public:
-   RGWFCGXFrontend(RGWProcessEnv& pe, RGWFrontendConfig *_conf) : RGWProcessFrontend(pe, _conf) {}
-   int init() {
-     pprocess = new RGWFCGXProcess(g_ceph_context, &env, g_conf->rgw_thread_pool_size, conf);
-     return 0;
-   }
- };
- class RGWLoadGenFrontend : public RGWProcessFrontend {
- public:
-   RGWLoadGenFrontend(RGWProcessEnv& pe, RGWFrontendConfig *_conf) : RGWProcessFrontend(pe, _conf) {}
-   int init() {
-     int num_threads;
-     conf->get_val("num_threads", g_conf->rgw_thread_pool_size, &num_threads);
-     RGWLoadGenProcess *pp = new RGWLoadGenProcess(g_ceph_context, &env, num_threads, conf);
-     pprocess = pp;
-     string uid_str;
-     conf->get_val("uid", "", &uid_str);
-     if (uid_str.empty()) {
-       derr << "ERROR: uid param must be specified for loadgen frontend" << dendl;
-       return EINVAL;
-     }
-     rgw_user uid(uid_str);
-     RGWUserInfo user_info;
-     int ret = rgw_get_user_info_by_uid(env.store, uid, user_info, NULL);
-     if (ret < 0) {
-       derr << "ERROR: failed reading user info: uid=" << uid << " ret=" << ret << dendl;
-       return ret;
-     }
-     map<string, RGWAccessKey>::iterator aiter = user_info.access_keys.begin();
-     if (aiter == user_info.access_keys.end()) {
-       derr << "ERROR: user has no S3 access keys set" << dendl;
-       return -EINVAL;
-     }
-     pp->set_access_key(aiter->second);
-     return 0;
-   }
- };
- class RGWMongooseFrontend : public RGWFrontend {
-   RGWFrontendConfig *conf;
-   struct mg_context *ctx;
-   RGWMongooseEnv env;
-   void set_conf_default(map<string, string>& m, const string& key, const string& def_val) {
-     if (m.find(key) == m.end()) {
-       m[key] = def_val;
-     }
-   }
- public:
-   RGWMongooseFrontend(RGWProcessEnv& pe, RGWFrontendConfig *_conf) : conf(_conf), ctx(NULL), env(pe) {
-   }
-   int init() {
-     return 0;
-   }
-   int run() {
-     char thread_pool_buf[32];
-     snprintf(thread_pool_buf, sizeof(thread_pool_buf), "%d", (int)g_conf->rgw_thread_pool_size);
-     string port_str;
-     map<string, string> conf_map = conf->get_config_map();
-     conf->get_val("port", "80", &port_str);
-     conf_map.erase("port");
-     conf_map["listening_ports"] = port_str;
-     set_conf_default(conf_map, "enable_keep_alive", "yes");
-     set_conf_default(conf_map, "num_threads", thread_pool_buf);
-     set_conf_default(conf_map, "decode_url", "no");
-     const char *options[conf_map.size() * 2 + 1];
-     int i = 0;
-     for (map<string, string>::iterator iter = conf_map.begin(); iter != conf_map.end(); ++iter) {
-       options[i] = iter->first.c_str();
-       options[i + 1] = iter->second.c_str();
-       dout(20)<< "civetweb config: " << options[i] << ": " << (options[i + 1] ? options[i + 1] : "<null>") << dendl;
-       i += 2;
-     }
-     options[i] = NULL;
-     struct mg_callbacks cb;
-     memset((void *)&cb, 0, sizeof(cb));
-     cb.begin_request = civetweb_callback;
-     cb.log_message = rgw_civetweb_log_callback;
-     cb.log_access = rgw_civetweb_log_access_callback;
-     ctx = mg_start(&cb, &env, (const char **)&options);
-     if (!ctx) {
-       return -EIO;
-     }
-     return 0;
-   }
-   void stop() {
-     if (ctx) {
-       mg_stop(ctx);
-     }
-   }
-   void join() {
-   }
-   void pause_for_new_config() override {
-     // block callbacks until unpause
-     env.mutex.get_write();
-   }
-   void unpause_with_new_config(RGWRados *store) override {
-     env.store = store;
-     // unpause callbacks
-     env.mutex.put_write();
-   }
- };
- // FrontendPauser implementation for RGWRealmReloader
- class RGWFrontendPauser : public RGWRealmReloader::Pauser {
-   std::list<RGWFrontend*> &frontends;
-   RGWRealmReloader::Pauser* pauser;
-  public:
-   RGWFrontendPauser(std::list<RGWFrontend*> &frontends,
-                     RGWRealmReloader::Pauser* pauser = nullptr)
-     : frontends(frontends), pauser(pauser) {}
-   void pause() override {
-     for (auto frontend : frontends)
-       frontend->pause_for_new_config();
-     if (pauser)
-       pauser->pause();
-   }
-   void resume(RGWRados *store) {
-     for (auto frontend : frontends)
-       frontend->unpause_with_new_config(store);
-     if (pauser)
-       pauser->resume(store);
-   }
- };
 +void intrusive_ptr_add_ref(CephContext* cct) { cct->get(); }
 +void intrusive_ptr_release(CephContext* cct) { cct->put(); }
 +
  /*
   * start up the RADOS connection and then handle HTTP messages as they come in
   */
index 12cf50567a7e7e47295d1f8e8478d7c5b2599415,df884086ec02b72dcd8045a75c96a2b424c5813b..f3c95ea976c9f3e8ca2f0b57c3ddd6fb34d24e19
@@@ -1709,11 -1641,13 +1675,13 @@@ static int forward_request_to_master(st
      ldout(s->cct, 0) << "rest connection is invalid" << dendl;
      return -EINVAL;
    }
 -  ldout(s->cct, 0) << "sending create_bucket request to master region" << dendl;
 +  ldout(s->cct, 0) << "sending create_bucket request to master zonegroup" << dendl;
    bufferlist response;
-   string uid_str = s->user.user_id.to_str();
+   string uid_str = s->user->user_id.to_str();
  #define MAX_REST_RESPONSE (128 * 1024) // we expect a very small response
-   int ret = store->rest_master_conn->forward(uid_str, s->info, objv, MAX_REST_RESPONSE, &in_data, &response);
+   int ret = store->rest_master_conn->forward(uid_str, s->info, objv,
+                                           MAX_REST_RESPONSE, &in_data,
+                                           &response);
    if (ret < 0)
      return ret;
  
@@@ -1812,10 -1747,10 +1781,10 @@@ void RGWCreateBucket::execute(
    if (s->bucket_exists) {
      string selected_placement_rule;
      rgw_bucket bucket;
-     op_ret = store->select_bucket_placement(s->user, zonegroup_id,
 -    op_ret = store->select_bucket_placement(*(s->user), region_name,
 -                                          placement_rule, s->bucket_tenant,
 -                                          s->bucket_name, bucket,
 -                                          &selected_placement_rule);
++    op_ret = store->select_bucket_placement(*(s->user), zonegroup_id,
 +                                          placement_rule,
 +                                          s->bucket_tenant, s->bucket_name,
 +                                          bucket, &selected_placement_rule);
      if (selected_placement_rule != s->bucket_info.placement_rule) {
        op_ret = -EEXIST;
        return;
    }
    s->bucket.tenant = s->bucket_tenant; /* ignored if bucket exists */
    s->bucket.name = s->bucket_name;
-   op_ret = store->create_bucket(s->user, s->bucket, zonegroup_id, placement_rule,
 -  op_ret = store->create_bucket(*(s->user), s->bucket, region_name,
 -                              placement_rule, attrs, info, pobjv, &ep_objv,
 -                              creation_time, pmaster_bucket, true);
++  op_ret = store->create_bucket(*(s->user), s->bucket, zonegroup_id, placement_rule,
 +                              attrs, info, pobjv, &ep_objv, creation_time,
 +                              pmaster_bucket, true);
-   /* continue if EEXIST and create_bucket will fail below.  this way we can recover
-    * from a partial create by retrying it. */
+   /* continue if EEXIST and create_bucket will fail below.  this way we can
+    * recover from a partial create by retrying it. */
    ldout(s->cct, 20) << "rgw_create_bucket returned ret=" << op_ret << " bucket=" << s->bucket << dendl;
  
    if (op_ret && op_ret != -EEXIST)
@@@ -2699,8 -2546,9 +2589,8 @@@ void RGWPutMetadataAccount::execute(
    }
  
    rgw_get_request_metadata(s->cct, s->info, attrs, false);
 -  rgw_get_user_attrs_by_uid(store, s->user->user_id, orig_attrs,
 -                          &acct_op_tracker);
 -  prepare_add_del_attrs(orig_attrs, rmattr_names, attrs, rmattrs);
 +  RGWUserInfo orig_uinfo;
-   rgw_get_user_info_by_uid(store, s->user.user_id, orig_uinfo, &acct_op_tracker);
++  rgw_get_user_info_by_uid(store, s->user->user_id, orig_uinfo, &acct_op_tracker);
    populate_with_generic_attrs(s, attrs);
  
    /* Handle the TempURL-related stuff. */
    }
  
    /* XXX tenant needed? */
-   op_ret = rgw_store_user_info(store, s->user, &orig_uinfo,
 -  op_ret = rgw_store_user_attrs(store, s->user->user_id.id, attrs, &rmattrs,
 -                              &acct_op_tracker);
++  op_ret = rgw_store_user_info(store, *(s->user), &orig_uinfo,
 +                               &acct_op_tracker, 0, false, &attrs);
    if (op_ret < 0) {
      return;
    }
Simple merge
index 0000000000000000000000000000000000000000,33bad85da908b64d6f607c71ddf3f9c6615e4c31..0cb74150584a6b6f0f3a266b99413b71e3e4c159
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,170 +1,179 @@@
+ // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+ // vim: ts=8 sw=2 smarttab
+ #ifndef RGW_PROCESS_H
+ #define RGW_PROCESS_H
+ #include "rgw_common.h"
+ #include "rgw_rados.h"
+ #include "rgw_acl.h"
+ #include "rgw_user.h"
+ #include "rgw_op.h"
+ #include "rgw_rest.h"
+ #include "common/WorkQueue.h"
+ #include "common/Throttle.h"
+ #if !defined(dout_subsys)
+ #define dout_subsys ceph_subsys_rgw
+ #define def_dout_subsys
+ #endif
+ #define SOCKET_BACKLOG 1024
+ extern void signal_shutdown();
+ struct RGWProcessEnv {
+   RGWRados *store;
+   RGWREST *rest;
+   OpsLogSocket *olog;
+   int port;
+ };
+ class RGWFrontendConfig;
+ class RGWProcess {
+   deque<RGWRequest*> m_req_queue;
+ protected:
+   CephContext *cct;
+   RGWRados* store;
+   OpsLogSocket* olog;
+   ThreadPool m_tp;
+   Throttle req_throttle;
+   RGWREST* rest;
+   RGWFrontendConfig* conf;
+   int sock_fd;
+   struct RGWWQ : public ThreadPool::WorkQueue<RGWRequest> {
+     RGWProcess* process;
+     RGWWQ(RGWProcess* p, time_t timeout, time_t suicide_timeout, ThreadPool* tp)
+       : ThreadPool::WorkQueue<RGWRequest>("RGWWQ", timeout, suicide_timeout,
+                                         tp), process(p) {}
+     bool _enqueue(RGWRequest* req) {
+       process->m_req_queue.push_back(req);
+       perfcounter->inc(l_rgw_qlen);
+       dout(20) << "enqueued request req=" << hex << req << dec << dendl;
+       _dump_queue();
+       return true;
+     }
+     void _dequeue(RGWRequest* req) {
+       assert(0);
+     }
+     bool _empty() {
+       return process->m_req_queue.empty();
+     }
+     RGWRequest* _dequeue() {
+       if (process->m_req_queue.empty())
+       return NULL;
+       RGWRequest *req = process->m_req_queue.front();
+       process->m_req_queue.pop_front();
+       dout(20) << "dequeued request req=" << hex << req << dec << dendl;
+       _dump_queue();
+       perfcounter->inc(l_rgw_qlen, -1);
+       return req;
+     }
+     using ThreadPool::WorkQueue<RGWRequest>::_process;
+     void _process(RGWRequest *req, ThreadPool::TPHandle &) override  {
+       perfcounter->inc(l_rgw_qactive);
+       process->handle_request(req);
+       process->req_throttle.put(1);
+       perfcounter->inc(l_rgw_qactive, -1);
+     }
+     void _dump_queue();
+     void _clear() {
+       assert(process->m_req_queue.empty());
+     }
+   } req_wq;
+ public:
+   RGWProcess(CephContext* cct, RGWProcessEnv* pe, int num_threads,
+           RGWFrontendConfig* _conf)
+     : cct(cct), store(pe->store), olog(pe->olog),
+       m_tp(cct, "RGWProcess::m_tp", "tp_rgw_process", num_threads),
+       req_throttle(cct, "rgw_ops", num_threads * 2),
+       rest(pe->rest), conf(_conf), sock_fd(-1),
+       req_wq(this, g_conf->rgw_op_thread_timeout,
+            g_conf->rgw_op_thread_suicide_timeout, &m_tp) {}
+   
+   virtual ~RGWProcess() {}
+   virtual void run() = 0;
+   virtual void handle_request(RGWRequest *req) = 0;
++  void pause() {
++    m_tp.pause();
++  }
++
++  void unpause_with_new_config(RGWRados *store) {
++    this->store = store;
++    m_tp.unpause();
++  }
++
+   void close_fd() {
+     if (sock_fd >= 0) {
+       ::close(sock_fd);
+       sock_fd = -1;
+     }
+   }
+ }; /* RGWProcess */
+ class RGWFCGXProcess : public RGWProcess {
+       int max_connections;
+ public:
+   /* have a bit more connections than threads so that requests are
+    * still accepted even if we're still processing older requests */
+   RGWFCGXProcess(CephContext* cct, RGWProcessEnv* pe, int num_threads,
+                RGWFrontendConfig* _conf)
+     : RGWProcess(cct, pe, num_threads, _conf),
+       max_connections(num_threads + (num_threads >> 3))
+     {}
+   void run();
+   void handle_request(RGWRequest* req);
+ };
+ class RGWProcessControlThread : public Thread {
+   RGWProcess *pprocess;
+ public:
+   RGWProcessControlThread(RGWProcess *_pprocess) : pprocess(_pprocess) {}
+   void *entry() {
+     pprocess->run();
+     return NULL;
+   }
+ };
+ class RGWLoadGenProcess : public RGWProcess {
+   RGWAccessKey access_key;
+ public:
+   RGWLoadGenProcess(CephContext* cct, RGWProcessEnv* pe, int num_threads,
+                 RGWFrontendConfig* _conf) :
+   RGWProcess(cct, pe, num_threads, _conf) {}
+   void run();
+   void checkpoint();
+   void handle_request(RGWRequest* req);
+   void gen_request(const string& method, const string& resource,
+                 int content_length, atomic_t* fail_flag);
+   void set_access_key(RGWAccessKey& key) { access_key = key; }
+ };
+ /* process stream request */
+ int process_request(RGWRados* store, RGWREST* rest, RGWRequest* req,
+                   RGWStreamIO* client_io, OpsLogSocket* olog);
+ #if defined(def_dout_subsys)
+ #undef def_dout_subsys
+ #undef dout_subsys
+ #endif
+ #endif /* RGW_PROCESS_H */
Simple merge
index 60d54c5b67355de9bfe9be6e4b9f4876ea007337,2478df2b204b11c8659ae1f2f75d669a15f6c1d0..4a9659346c56e38d3869b733723087a4f836b14a
@@@ -3038,9 -2449,7 +3038,9 @@@ public
                         const char *if_match = NULL, const char *if_nomatch = NULL);
  
    CephContext *ctx();
- };
 +
 +  bool is_canceled() { return canceled; }
+ }; /* RGWPutObjProcessor */
  
  struct put_obj_aio_info {
    void *handle;
Simple merge
Simple merge
index e0c1fd6fecb415686e50f98b1b27bffcdb94f801,143e4cb8705c266706a773eaccd62575abb8960c..fe7f0272b0c29fb17d46860d0887da2e43ce183f
   * Foundation. See file COPYING.
   *
   */
- #ifndef CEPH_RGW_REST_CONFIG_H
- #define CEPH_RGW_REST_CONFIG_H
+ #ifndef RGW_REST_CONFIG_H
+ #define RGW_REST_CONFIG_H
  
 -class RGWOp_RegionMap_Get : public RGWRESTOp {
 -  RGWRegionMap regionmap;
 +class RGWOp_ZoneGroupMap_Get : public RGWRESTOp {
 +  RGWZoneGroupMap zonegroup_map;
 +  bool old_format;
  public:
 -  RGWOp_RegionMap_Get() {}
 -  ~RGWOp_RegionMap_Get() {}
 +  RGWOp_ZoneGroupMap_Get(bool _old_format):old_format(_old_format) {}
 +  ~RGWOp_ZoneGroupMap_Get() {}
  
    int verify_permission() {
      return 0; 
Simple merge
index 2f45db99009cc381eaab273f03be6c1499e3c447,0000000000000000000000000000000000000000..176802b1d3905560826730567bbae39a7bab7872
mode 100644,000000..100644
--- /dev/null
@@@ -1,278 -1,0 +1,278 @@@
-   RGWHandler* get_handler(struct req_state*) override {
 +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 +// vim: ts=8 sw=2 smarttab
 +
 +#include "common/errno.h"
 +#include "rgw_rest_realm.h"
 +#include "rgw_rest_s3.h"
 +#include "rgw_rest_config.h"
 +
 +#define dout_subsys ceph_subsys_rgw
 +
 +// reject 'period push' if we would have to fetch too many intermediate periods
 +static const uint32_t PERIOD_HISTORY_FETCH_MAX = 64;
 +
 +// base period op, shared between Get and Post
 +class RGWOp_Period_Base : public RGWRESTOp {
 + protected:
 +  RGWPeriod period;
 + public:
 +  int verify_permission() override { return 0; }
 +  void send_response() override;
 +};
 +
 +// reply with the period object on success
 +void RGWOp_Period_Base::send_response()
 +{
 +  set_req_state_err(s, http_ret);
 +  dump_errno(s);
 +  end_header(s);
 +
 +  if (http_ret < 0)
 +    return;
 +
 +  encode_json("period", period, s->formatter);
 +  flusher.flush();
 +}
 +
 +// GET /admin/realm/period
 +class RGWOp_Period_Get : public RGWOp_Period_Base {
 + public:
 +  void execute() override;
 +  const string name() override { return "get_period"; }
 +};
 +
 +void RGWOp_Period_Get::execute()
 +{
 +  string realm_id, realm_name, period_id;
 +  epoch_t epoch = 0;
 +  RESTArgs::get_string(s, "realm_id", realm_id, &realm_id);
 +  RESTArgs::get_string(s, "realm_name", realm_name, &realm_name);
 +  RESTArgs::get_string(s, "period_id", period_id, &period_id);
 +  RESTArgs::get_uint32(s, "epoch", 0, &epoch);
 +
 +  period.set_id(period_id);
 +  period.set_epoch(epoch);
 +
 +  http_ret = period.init(store->ctx(), store, realm_id, realm_name);
 +  if (http_ret < 0)
 +    ldout(store->ctx(), 5) << "failed to read period" << dendl;
 +}
 +
 +// POST /admin/realm/period
 +class RGWOp_Period_Post : public RGWOp_Period_Base {
 + public:
 +  void execute() override;
 +  const string name() override { return "post_period"; }
 +};
 +
 +void RGWOp_Period_Post::execute()
 +{
 +  auto cct = store->ctx();
 +
 +  // initialize the period without reading from rados
 +  period.init(cct, store, false);
 +
 +  // decode the period from input
 +#define PERIOD_MAX_LEN 4096
 +  bool empty;
 +  http_ret = rgw_rest_get_json_input(cct, s, period, PERIOD_MAX_LEN, &empty);
 +  if (http_ret < 0) {
 +    lderr(cct) << "failed to decode period" << dendl;
 +    return;
 +  }
 +
 +  // require period.realm_id to match our realm
 +  if (period.get_realm() != store->realm.get_id()) {
 +    lderr(cct) << "period with realm id " << period.get_realm()
 +        << " doesn't match current realm " << store->realm.get_id() << dendl;
 +    http_ret = -EINVAL;
 +    return;
 +  }
 +
 +  // load the realm and current period from rados; there may be a more recent
 +  // period that we haven't restarted with yet. we also don't want to modify
 +  // the objects in use by RGWRados
 +  RGWRealm realm(period.get_realm());
 +  http_ret = realm.init(cct, store);
 +  if (http_ret < 0) {
 +    lderr(cct) << "failed to read current realm: "
 +        << cpp_strerror(-http_ret) << dendl;
 +    return;
 +  }
 +
 +  RGWPeriod current_period;
 +  http_ret = current_period.init(cct, store, realm.get_id());
 +  if (http_ret < 0) {
 +    lderr(cct) << "failed to read current period: "
 +        << cpp_strerror(-http_ret) << dendl;
 +    return;
 +  }
 +
 +  // if period id is empty, handle as 'period commit'
 +  if (period.get_id().empty()) {
 +    http_ret = period.commit(realm, current_period);
 +    if (http_ret < 0) {
 +      lderr(cct) << "master zone failed to commit period" << dendl;
 +    }
 +    return;
 +  }
 +
 +  // if it's not period commit, nobody is allowed to push to the master zone
 +  if (period.get_master_zone() == store->get_zone_params().get_id()) {
 +    ldout(cct, 10) << "master zone rejecting period id="
 +        << period.get_id() << " epoch=" << period.get_epoch() << dendl;
 +    http_ret = -EINVAL; // XXX: error code
 +    return;
 +  }
 +
 +  // write the period to rados
 +  http_ret = period.store_info(false);
 +  if (http_ret < 0) {
 +    lderr(cct) << "failed to store period " << period.get_id() << dendl;
 +    return;
 +  }
 +
 +  // decide whether we can set_current_period() or set_latest_epoch()
 +  if (period.get_id() != current_period.get_id()) {
 +    auto current_epoch = current_period.get_realm_epoch();
 +    // discard periods in the past
 +    if (period.get_realm_epoch() < current_epoch) {
 +      ldout(cct, 10) << "discarding period " << period.get_id()
 +          << " with realm epoch " << period.get_realm_epoch()
 +          << " older than current epoch " << current_epoch << dendl;
 +      // return success to ack that we have this period
 +      return;
 +    }
 +    // discard periods too far in the future
 +    if (period.get_realm_epoch() > current_epoch + PERIOD_HISTORY_FETCH_MAX) {
 +      lderr(cct) << "discarding period " << period.get_id()
 +          << " with realm epoch " << period.get_realm_epoch() << " too far in "
 +          "the future from current epoch " << current_epoch << dendl;
 +      http_ret = -ENOENT; // XXX: error code
 +      return;
 +    }
 +    // attach a copy of the period into the period history
 +    auto cursor = store->period_history->attach(RGWPeriod{period});
 +    if (!cursor) {
 +      // we're missing some history between the new period and current_period
 +      http_ret = cursor.get_error();
 +      lderr(cct) << "failed to collect the periods between current period "
 +          << current_period.get_id() << " (realm epoch " << current_epoch
 +          << ") and the new period " << period.get_id()
 +          << " (realm epoch " << period.get_realm_epoch()
 +          << "): " << cpp_strerror(-http_ret) << dendl;
 +      return;
 +    }
 +    if (cursor.has_next()) {
 +      // don't switch if we have a newer period in our history
 +      ldout(cct, 4) << "attached period " << period.get_id()
 +          << " to history, but the history contains newer periods" << dendl;
 +      return;
 +    }
 +    // set as current period
 +    http_ret = realm.set_current_period(period);
 +    if (http_ret < 0) {
 +      lderr(cct) << "failed to update realm's current period" << dendl;
 +      return;
 +    }
 +    ldout(cct, 4) << "period " << period.get_id()
 +        << " is newer than current period " << current_period.get_id()
 +        << ", updating realm's current period and notifying zone" << dendl;
 +    realm.notify_new_period(period);
 +    return;
 +  }
 +
 +  if (period.get_epoch() <= current_period.get_epoch()) {
 +    lderr(cct) << "period epoch " << period.get_epoch() << " is not newer "
 +        "than current epoch " << current_period.get_epoch()
 +        << ", discarding update" << dendl;
 +    return;
 +  }
 +  // set as latest epoch
 +  http_ret = period.set_latest_epoch(period.get_epoch());
 +  if (http_ret < 0) {
 +    lderr(cct) << "failed to set latest epoch" << dendl;
 +    return;
 +  }
 +  // reflect the period into our local objects
 +  http_ret  = period.reflect();
 +  if (http_ret  < 0) {
 +    lderr(cct) << "failed to update local objects: "
 +        << cpp_strerror(-http_ret) << dendl;
 +    return;
 +  }
 +  ldout(cct, 4) << "period epoch " << period.get_epoch()
 +      << " is newer than current epoch " << current_period.get_epoch()
 +      << ", updating period's latest epoch and notifying zone" << dendl;
 +  realm.notify_new_period(period);
 +  // update the period history
 +  store->period_history->insert(RGWPeriod{period});
 +}
 +
 +class RGWHandler_Period : public RGWHandler_Auth_S3 {
 + protected:
 +  RGWOp *op_get() override { return new RGWOp_Period_Get; }
 +  RGWOp *op_post() override { return new RGWOp_Period_Post; }
 +};
 +
 +class RGWRESTMgr_Period : public RGWRESTMgr {
 + public:
- RGWHandler* RGWRESTMgr_Realm::get_handler(struct req_state*)
++  RGWHandler_REST* get_handler(struct req_state*) override {
 +    return new RGWHandler_Period;
 +  }
 +};
 +
 +
 +// GET /admin/realm
 +class RGWOp_Realm_Get : public RGWRESTOp {
 +  std::unique_ptr<RGWRealm> realm;
 +public:
 +  int verify_permission() override { return 0; }
 +  void execute() override;
 +  void send_response() override;
 +  const string name() { return "get_realm"; }
 +};
 +
 +void RGWOp_Realm_Get::execute()
 +{
 +  string id;
 +  RESTArgs::get_string(s, "id", id, &id);
 +  string name;
 +  RESTArgs::get_string(s, "name", name, &name);
 +
 +  // read realm
 +  realm.reset(new RGWRealm(id, name));
 +  http_ret = realm->init(g_ceph_context, store);
 +  if (http_ret < 0)
 +    lderr(store->ctx()) << "failed to read realm id=" << id
 +        << " name=" << name << dendl;
 +}
 +
 +void RGWOp_Realm_Get::send_response()
 +{
 +  set_req_state_err(s, http_ret);
 +  dump_errno(s);
 +  end_header(s);
 +
 +  if (http_ret < 0)
 +    return;
 +
 +  encode_json("realm", *realm, s->formatter);
 +  flusher.flush();
 +}
 +
 +class RGWHandler_Realm : public RGWHandler_Auth_S3 {
 +protected:
 +  RGWOp *op_get() { return new RGWOp_Realm_Get; }
 +};
 +
 +RGWRESTMgr_Realm::RGWRESTMgr_Realm()
 +{
 +  // add the /admin/realm/period resource
 +  register_resource("period", new RGWRESTMgr_Period);
 +}
 +
++RGWHandler_REST* RGWRESTMgr_Realm::get_handler(struct req_state*)
 +{
 +  return new RGWHandler_Realm;
 +}
index d53e0607e7ee52fc6445a72ae825466844608e85,0000000000000000000000000000000000000000..cb61594595d8ee0a4079fae2aacf78622190df68
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
-   RGWHandler* get_handler(struct req_state*) override;
 +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 +// vim: ts=8 sw=2 smarttab
 +
 +#ifndef CEPH_RGW_REST_REALM_H
 +#define CEPH_RGW_REST_REALM_H
 +
 +#include "rgw_rest.h"
 +
 +class RGWRESTMgr_Realm : public RGWRESTMgr {
 +public:
 +  RGWRESTMgr_Realm();
 +
++  RGWHandler_REST* get_handler(struct req_state*) override;
 +};
 +
 +#endif
index fb4fe0835ccc2ec14f117f78949e31a4cdb8b825,9cbbd860aace23c12b2af4b3d86e5b97a189f9d7..68b26c9fa6d3199ab1a6b08650e80f97590c7bfd
@@@ -105,29 -105,8 +105,30 @@@ int RGWGetObj_ObjStore_S3::send_respons
    return send_response_data(bl, 0 , 0);
  }
  
- int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs, off_t bl_len)
 +template <class T>
 +int decode_attr_bl_single_value(map<string, bufferlist>& attrs, const char *attr_name, T *result, T def_val)
 +{
 +  map<string, bufferlist>::iterator iter = attrs.find(attr_name);
 +  if (iter == attrs.end()) {
 +    *result = def_val;
 +    return 0;
 +  }
 +  bufferlist& bl = iter->second;
 +  if (bl.length() == 0) {
 +    *result = def_val;
 +    return 0;
 +  }
 +  bufferlist::iterator bliter = bl.begin();
 +  try {
 +    ::decode(*result, bliter);
 +  } catch (buffer::error& err) {
 +    return -EIO;
 +  }
 +  return 0;
 +}
 +
+ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
+                                             off_t bl_len)
  {
    const char *content_type = NULL;
    string content_type_str;
    if (s->system_request && lastmod) {
      /* we end up dumping mtime in two different methods, a bit redundant */
      dump_epoch_header(s, "Rgwx-Mtime", lastmod);
-     s->cio->print("Rgwx-Obj-PG-Ver: %lld\r\n", (long long)pg_ver);
 +    uint64_t pg_ver;
 +    int r = decode_attr_bl_single_value(attrs, RGW_ATTR_PG_VER, &pg_ver, (uint64_t)0);
 +    if (r < 0) {
 +      ldout(s->cct, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
 +    }
-       s->cio->print("Rgwx-Source-Zone-Short-Id: %lld\r\n", (long long)source_zone_short_id);
++    STREAM_IO(s)->print("Rgwx-Obj-PG-Ver: %lld\r\n", (long long)pg_ver);
 +
 +    uint32_t source_zone_short_id;
 +    r = decode_attr_bl_single_value(attrs, RGW_ATTR_SOURCE_ZONE, &source_zone_short_id, (uint32_t)0);
 +    if (r < 0) {
 +      ldout(s->cct, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
 +    }
 +    if (source_zone_short_id != 0) {
++      STREAM_IO(s)->print("Rgwx-Source-Zone-Short-Id: %lld\r\n", (long long)source_zone_short_id);
 +    }
    }
  
    dump_content_length(s, total_len);
@@@ -361,27 -310,22 +365,28 @@@ void RGWListBucket_ObjStore_S3::send_ve
      vector<RGWObjEnt>::iterator iter;
      for (iter = objs.begin(); iter != objs.end(); ++iter) {
        time_t mtime = iter->mtime.sec();
-       const char *section_name = (iter->is_delete_marker() ? "DeleteMarker" : "Version");
-       s->formatter->open_object_section(section_name);
+       const char *section_name = (iter->is_delete_marker() ? "DeleteMarker"
+                                 : "Version");
+       s->formatter->open_array_section(section_name);
 +      if (objs_container) {
 +        s->formatter->dump_bool("IsDeleteMarker", iter->is_delete_marker());
 +      }
        if (encode_key) {
-         string key_name;
-         url_encode(iter->key.name, key_name);
-         s->formatter->dump_string("Key", key_name);
+       string key_name;
+       url_encode(iter->key.name, key_name);
+       s->formatter->dump_string("Key", key_name);
        } else {
-         s->formatter->dump_string("Key", iter->key.name);
+       s->formatter->dump_string("Key", iter->key.name);
        }
        string version_id = iter->key.instance;
        if (version_id.empty()) {
-         version_id = "null";
+       version_id = "null";
        }
 -      if (s->system_request && iter->versioned_epoch > 0) {
 -      s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
 +      if (s->system_request) {
 +        if (iter->versioned_epoch > 0) {
 +          s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
 +        }
 +        s->formatter->dump_string("RgwxTag", iter->tag);
        }
        s->formatter->dump_string("VersionId", version_id);
        s->formatter->dump_bool("IsLatest", iter->is_current());
        dump_owner(s, iter->owner, iter->owner_display_name);
        s->formatter->close_section();
      }
 +    if (objs_container) {
 +      s->formatter->close_section();
 +    }
 +
      if (!common_prefixes.empty()) {
        map<string, bool>::iterator pref_iter;
-       for (pref_iter = common_prefixes.begin(); pref_iter != common_prefixes.end(); ++pref_iter) {
-         s->formatter->open_array_section("CommonPrefixes");
-         s->formatter->dump_string("Prefix", pref_iter->first);
-         s->formatter->close_section();
+       for (pref_iter = common_prefixes.begin();
+          pref_iter != common_prefixes.end(); ++pref_iter) {
+       s->formatter->open_array_section("CommonPrefixes");
+       s->formatter->dump_string("Prefix", pref_iter->first);
+       s->formatter->close_section();
        }
      }
    }
@@@ -2913,13 -2858,16 +2950,16 @@@ int RGWHandler_Auth_S3::init(RGWRados *
    if (ret < 0)
      return ret;
  
-   return RGWHandler_ObjStore::init(store, state, cio);
+   return RGWHandler_REST::init(store, state, cio);
  }
  
- RGWHandler *RGWRESTMgr_S3::get_handler(struct req_state *s)
+ RGWHandler_REST* RGWRESTMgr_S3::get_handler(struct req_state *s)
  {
    bool is_s3website = enable_s3website && (s->prot_flags & RGW_REST_WEBSITE);
-   int ret = RGWHandler_ObjStore_S3::init_from_header(s, is_s3website ? RGW_FORMAT_HTML : RGW_FORMAT_XML, true);
+   int ret =
+     RGWHandler_REST_S3::init_from_header(s,
+                                       is_s3website ? RGW_FORMAT_HTML :
 -                                      RGW_FORMAT_XML, false);
++                                      RGW_FORMAT_XML, true);
    if (ret < 0)
      return NULL;
  
index b718a27827513d6e1a9b92abc442eda29f1a9aff,129934a9fdb253377fc64b5eff9b6b638566f9de..a97b82fe1cddfd3403c9ff45d1161619a38f00d0
@@@ -415,12 -413,12 +416,11 @@@ class RGWHandler_REST_S3 : public RGWHa
  public:
    static int init_from_header(struct req_state *s, int default_formatter, bool configurable_format);
  
-   RGWHandler_ObjStore_S3() : RGWHandler_ObjStore() {}
-   virtual ~RGWHandler_ObjStore_S3() {}
+   RGWHandler_REST_S3() : RGWHandler_REST() {}
+   virtual ~RGWHandler_REST_S3() {}
 -  int validate_bucket_name(const string& bucket, bool relaxed_names) = delete;
+   int get_errordoc(const string& errordoc_key, string* error_content);  
  
-   int validate_bucket_name(const string& bucket, bool relaxed_names);
-   using RGWHandler_ObjStore::validate_bucket_name;
-   
    virtual int init(RGWRados *store, struct req_state *s, RGWClientIO *cio);
    virtual int authorize() {
      return RGW_Auth_S3::authorize(store, s);
index 45e915f1dd9913fe006cc304847e29d45aeace97,0df6200fc10708a49e17805ff68476c3edb92a02..d12f8650142434365972d20d303eccd727bf5ad5
@@@ -456,10 -469,10 +469,10 @@@ int RGWCreateBucket_ObjStore_SWIFT::get
    }
  
    if (!has_policy) {
-     policy.create_default(s->user.user_id, s->user.display_name);
+     policy.create_default(s->user->user_id, s->user->display_name);
    }
  
 -  location_constraint = store->region.api_name;
 +  location_constraint = store->get_zonegroup().api_name;
    placement_rule = s->info.env->get("HTTP_X_STORAGE_POLICY", "");
  
    return 0;
Simple merge
Simple merge
Simple merge
index d1b8d8d369a4e2091171c8b91daa6b9a85d3345d,c205cb8148604cda38d6750806ba91e7513fd3bb..0c2efbd64731df29eb240a76bf5528d2d1baa90d
@@@ -662,24 -642,23 +651,24 @@@ bin_DEBUGPROGRAMS += ceph_test_rgw_ob
  
  ceph_test_cls_rgw_meta_SOURCES = test/test_rgw_admin_meta.cc
  ceph_test_cls_rgw_meta_LDADD = \
 -      $(LIBRADOS) $(LIBRGW) $(CEPH_GLOBAL) \
 +      $(LIBRGW) $(LIBRADOS) $(CEPH_GLOBAL) \
        $(UNITTEST_LDADD) $(CRYPTO_LIBS) \
        -lcurl -lexpat \
-       libcls_version_client.a libcls_log_client.a\
-       libcls_statelog_client.a libcls_refcount_client.la \
-       libcls_rgw_client.la libcls_user_client.a libcls_lock_client.la
 +      libcls_timeindex_client.a \
+       libcls_version_client.la libcls_log_client.la \
+       libcls_statelog_client.la libcls_refcount_client.la \
+       libcls_rgw_client.la libcls_user_client.la libcls_lock_client.la
  ceph_test_cls_rgw_meta_CXXFLAGS = $(UNITTEST_CXXFLAGS)
  bin_DEBUGPROGRAMS += ceph_test_cls_rgw_meta
  
  ceph_test_cls_rgw_log_SOURCES = test/test_rgw_admin_log.cc
  ceph_test_cls_rgw_log_LDADD = \
 -      $(LIBRADOS) $(LIBRGW) $(CEPH_GLOBAL) \
 +      $(LIBRADOS) $(LIBRGW) $(LIBRGW_DEPS) $(CEPH_GLOBAL) \
        $(UNITTEST_LDADD) $(CRYPTO_LIBS) \
        -lcurl -lexpat \
-       libcls_version_client.a libcls_log_client.a \
-       libcls_statelog_client.a libcls_refcount_client.la \
-       libcls_rgw_client.la libcls_user_client.a libcls_lock_client.la
+       libcls_version_client.la libcls_log_client.la \
+       libcls_statelog_client.la libcls_refcount_client.la \
+       libcls_rgw_client.la libcls_user_client.la libcls_lock_client.la
  ceph_test_cls_rgw_log_CXXFLAGS = $(UNITTEST_CXXFLAGS)
  bin_DEBUGPROGRAMS += ceph_test_cls_rgw_log
  
Simple merge