bool DaemonServer::handle_command(MCommand *m)
{
  Mutex::Locker l(lock);
  int r = 0;
  std::stringstream ss;
  std::string prefix;

  assert(lock.is_locked_by_me());

  /**
   * The working data for processing an MCommand.  This lives in
   * a class to enable passing it into other threads for processing
   * outside of the thread/locks that called handle_command.
   */
  class CommandContext
  {
    public:
    MCommand *m;
    bufferlist odata;
    cmdmap_t cmdmap;

    CommandContext(MCommand *m_)
      : m(m_)
    {
    }

    ~CommandContext()
    {
      m->put();
    }

    void reply(int r, const std::stringstream &ss)
    {
      reply(r, ss.str());
    }

    void reply(int r, const std::string &rs)
    {
      // Let the connection drop as soon as we've sent our response
      ConnectionRef con = m->get_connection();
      if (con) {
        con->mark_disposable();
      }

      dout(1) << "handle_command " << cpp_strerror(r) << " " << rs << dendl;
      if (con) {
        MCommandReply *reply = new MCommandReply(r, rs);
        reply->set_tid(m->get_tid());
        reply->set_data(odata);
        con->send_message(reply);
      }
    }
  };

  /**
   * A context for receiving a bufferlist/error string from a background
   * function and then calling back to a CommandContext when it's done
   */
  class ReplyOnFinish : public Context {
    std::shared_ptr<CommandContext> cmdctx;

  public:
    bufferlist from_mon;
    string outs;

    ReplyOnFinish(std::shared_ptr<CommandContext> cmdctx_)
      : cmdctx(cmdctx_)
    {}
    void finish(int r) override {
      cmdctx->odata.claim_append(from_mon);
      cmdctx->reply(r, outs);
    }
  };

  std::shared_ptr<CommandContext> cmdctx = std::make_shared<CommandContext>(m);

  MgrSessionRef session(static_cast<MgrSession*>(m->get_connection()->get_priv()));
  if (!session) {
    return true;
  }
  session->put(); // SessionRef takes a ref
  if (session->inst.name == entity_name_t())
    session->inst.name = m->get_source();

  std::string format;
  boost::scoped_ptr<Formatter> f;
  map<string,string> param_str_map;

  if (!cmdmap_from_json(m->cmd, &(cmdctx->cmdmap), ss)) {
    cmdctx->reply(-EINVAL, ss);
    return true;
  }

  {
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "format", format, string("plain"));
    f.reset(Formatter::create(format));
  }

  cmd_getval(cct, cmdctx->cmdmap, "prefix", prefix);

  dout(4) << "decoded " << cmdctx->cmdmap.size() << dendl;
  dout(4) << "prefix=" << prefix << dendl;

  if (prefix == "get_command_descriptions") {
    dout(10) << "reading commands from python modules" << dendl;
    const auto py_commands = py_modules.get_commands();

    int cmdnum = 0;
    JSONFormatter f;
    f.open_object_section("command_descriptions");

    auto dump_cmd = [&cmdnum, &f](const MonCommand &mc){
      ostringstream secname;
      secname << "cmd" << setfill('0') << std::setw(3) << cmdnum;
      dump_cmddesc_to_json(&f, secname.str(), mc.cmdstring, mc.helpstring,
                           mc.module, mc.req_perms, mc.availability, 0);
      cmdnum++;
    };

    for (const auto &pyc : py_commands) {
      dump_cmd(pyc);
    }

    for (const auto &mgr_cmd : mgr_commands) {
      dump_cmd(mgr_cmd);
    }

    f.close_section();	// command_descriptions
    f.flush(cmdctx->odata);
    cmdctx->reply(0, ss);
    return true;
  }

  // lookup command
  const MonCommand *mgr_cmd = _get_mgrcommand(prefix, mgr_commands);
  _generate_command_map(cmdctx->cmdmap, param_str_map);
  if (!mgr_cmd) {
    MonCommand py_command = {"", "", "py", "rw", "cli"};
    if (!_allowed_command(session.get(), py_command.module, prefix, cmdctx->cmdmap,
                          param_str_map, &py_command)) {
      dout(1) << " access denied" << dendl;
      ss << "access denied; does your client key have mgr caps?"
	" See http://docs.ceph.com/docs/master/mgr/administrator/#client-authentication";
      cmdctx->reply(-EACCES, ss);
      return true;
    }
  } else {
    // validate user's permissions for requested command
    if (!_allowed_command(session.get(), mgr_cmd->module, prefix, cmdctx->cmdmap,
                          param_str_map, mgr_cmd)) {
      dout(1) << " access denied" << dendl;
      audit_clog->info() << "from='" << session->inst << "' "
                         << "entity='" << session->entity_name << "' "
                         << "cmd=" << m->cmd << ":  access denied";
      ss << "access denied' does your client key have mgr caps?"
	" See http://docs.ceph.com/docs/master/mgr/administrator/#client-authentication";
      cmdctx->reply(-EACCES, ss);
      return true;
    }
  }

  audit_clog->debug()
    << "from='" << session->inst << "' "
    << "entity='" << session->entity_name << "' "
    << "cmd=" << m->cmd << ": dispatch";

  // ----------------
  // service map commands
  if (prefix == "service dump") {
    if (!f)
      f.reset(Formatter::create("json-pretty"));
    cluster_state.with_servicemap([&](const ServiceMap &service_map) {
	f->dump_object("service_map", service_map);
      });
    f->flush(cmdctx->odata);
    cmdctx->reply(0, ss);
    return true;
  }
  if (prefix == "service status") {
    if (!f)
      f.reset(Formatter::create("json-pretty"));
    // only include state from services that are in the persisted service map
    f->open_object_section("service_status");
    ServiceMap s;
    cluster_state.with_servicemap([&](const ServiceMap& service_map) {
	s = service_map;
      });
    for (auto& p : s.services) {
      f->open_object_section(p.first.c_str());
      for (auto& q : p.second.daemons) {
	f->open_object_section(q.first.c_str());
	DaemonKey key(p.first, q.first);
	assert(daemon_state.exists(key));
	auto daemon = daemon_state.get(key);
	Mutex::Locker l(daemon->lock);
	f->dump_stream("status_stamp") << daemon->service_status_stamp;
	f->dump_stream("last_beacon") << daemon->last_service_beacon;
	f->open_object_section("status");
	for (auto& r : daemon->service_status) {
	  f->dump_string(r.first.c_str(), r.second);
	}
	f->close_section();
	f->close_section();
      }
      f->close_section();
    }
    f->close_section();
    f->flush(cmdctx->odata);
    cmdctx->reply(0, ss);
    return true;
  }

  if (prefix == "config set") {
    std::string key;
    std::string val;
    cmd_getval(cct, cmdctx->cmdmap, "key", key);
    cmd_getval(cct, cmdctx->cmdmap, "value", val);
    r = cct->_conf->set_val(key, val, true, &ss);
    if (r == 0) {
      cct->_conf->apply_changes(nullptr);
    }
    cmdctx->reply(0, ss);
    return true;
  }

  // -----------
  // PG commands

  if (prefix == "pg scrub" ||
      prefix == "pg repair" ||
      prefix == "pg deep-scrub") {
    string scrubop = prefix.substr(3, string::npos);
    pg_t pgid;
    string pgidstr;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "pgid", pgidstr);
    if (!pgid.parse(pgidstr.c_str())) {
      ss << "invalid pgid '" << pgidstr << "'";
      cmdctx->reply(-EINVAL, ss);
      return true;
    }
    bool pg_exists = false;
    cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	pg_exists = osdmap.pg_exists(pgid);
      });
    if (!pg_exists) {
      ss << "pg " << pgid << " dne";
      cmdctx->reply(-ENOENT, ss);
      return true;
    }
    int acting_primary = -1;
    cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	acting_primary = osdmap.get_pg_acting_primary(pgid);
      });
    if (acting_primary == -1) {
      ss << "pg " << pgid << " has no primary osd";
      cmdctx->reply(-EAGAIN, ss);
      return true;
    }
    auto p = osd_cons.find(acting_primary);
    if (p == osd_cons.end()) {
      ss << "pg " << pgid << " primary osd." << acting_primary
	 << " is not currently connected";
      cmdctx->reply(-EAGAIN, ss);
    }
    vector<pg_t> pgs = { pgid };
    for (auto& con : p->second) {
      con->send_message(new MOSDScrub(monc->get_fsid(),
				      pgs,
				      scrubop == "repair",
				      scrubop == "deep-scrub"));
    }
    ss << "instructing pg " << pgid << " on osd." << acting_primary
       << " to " << scrubop;
    cmdctx->reply(0, ss);
    return true;
  } else if (prefix == "osd scrub" ||
	      prefix == "osd deep-scrub" ||
	      prefix == "osd repair") {
    string whostr;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "who", whostr);
    vector<string> pvec;
    get_str_vec(prefix, pvec);

    set<int> osds;
    if (whostr == "*" || whostr == "all" || whostr == "any") {
      cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	  for (int i = 0; i < osdmap.get_max_osd(); i++)
	    if (osdmap.is_up(i)) {
	      osds.insert(i);
	    }
	});
    } else {
      long osd = parse_osd_id(whostr.c_str(), &ss);
      if (osd < 0) {
	ss << "invalid osd '" << whostr << "'";
	cmdctx->reply(-EINVAL, ss);
	return true;
      }
      cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	  if (osdmap.is_up(osd)) {
	    osds.insert(osd);
	  }
	});
      if (osds.empty()) {
	ss << "osd." << osd << " is not up";
	cmdctx->reply(-EAGAIN, ss);
	return true;
      }
    }
    set<int> sent_osds, failed_osds;
    for (auto osd : osds) {
      auto p = osd_cons.find(osd);
      if (p == osd_cons.end()) {
	failed_osds.insert(osd);
      } else {
	sent_osds.insert(osd);
	for (auto& con : p->second) {
	  con->send_message(new MOSDScrub(monc->get_fsid(),
					  pvec.back() == "repair",
					  pvec.back() == "deep-scrub"));
	}
      }
    }
    if (failed_osds.size() == osds.size()) {
      ss << "failed to instruct osd(s) " << osds << " to " << pvec.back()
	 << " (not connected)";
      r = -EAGAIN;
    } else {
      ss << "instructed osd(s) " << sent_osds << " to " << pvec.back();
      if (!failed_osds.empty()) {
	ss << "; osd(s) " << failed_osds << " were not connected";
      }
      r = 0;
    }
    cmdctx->reply(0, ss);
    return true;
  } else if (prefix == "osd reweight-by-pg" ||
	     prefix == "osd reweight-by-utilization" ||
	     prefix == "osd test-reweight-by-pg" ||
	     prefix == "osd test-reweight-by-utilization") {
    bool by_pg =
      prefix == "osd reweight-by-pg" || prefix == "osd test-reweight-by-pg";
    bool dry_run =
      prefix == "osd test-reweight-by-pg" ||
      prefix == "osd test-reweight-by-utilization";
    int64_t oload;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "oload", oload, int64_t(120));
    set<int64_t> pools;
    vector<string> poolnames;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "pools", poolnames);
    cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	for (const auto& poolname : poolnames) {
	  int64_t pool = osdmap.lookup_pg_pool_name(poolname);
	  if (pool < 0) {
	    ss << "pool '" << poolname << "' does not exist";
	    r = -ENOENT;
	  }
	  pools.insert(pool);
	}
      });
    if (r) {
      cmdctx->reply(r, ss);
      return true;
    }
    double max_change = g_conf->mon_reweight_max_change;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "max_change", max_change);
    if (max_change <= 0.0) {
      ss << "max_change " << max_change << " must be positive";
      cmdctx->reply(-EINVAL, ss);
      return true;
    }
    int64_t max_osds = g_conf->mon_reweight_max_osds;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "max_osds", max_osds);
    if (max_osds <= 0) {
      ss << "max_osds " << max_osds << " must be positive";
      cmdctx->reply(-EINVAL, ss);
      return true;
    }
    string no_increasing;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "no_increasing", no_increasing);
    string out_str;
    mempool::osdmap::map<int32_t, uint32_t> new_weights;
    r = cluster_state.with_pgmap([&](const PGMap& pgmap) {
	return cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	    return reweight::by_utilization(osdmap, pgmap,
					    oload,
					    max_change,
					    max_osds,
					    by_pg,
					    pools.empty() ? NULL : &pools,
					    no_increasing == "--no-increasing",
					    &new_weights,
					    &ss, &out_str, f.get());
	  });
      });
    if (r >= 0) {
      dout(10) << "reweight::by_utilization: finished with " << out_str << dendl;
    }
    if (f) {
      f->flush(cmdctx->odata);
    } else {
      cmdctx->odata.append(out_str);
    }
    if (r < 0) {
      ss << "FAILED reweight-by-pg";
      cmdctx->reply(r, ss);
      return true;
    } else if (r == 0 || dry_run) {
      ss << "no change";
      cmdctx->reply(r, ss);
      return true;
    } else {
      json_spirit::Object json_object;
      for (const auto& osd_weight : new_weights) {
	json_spirit::Config::add(json_object,
				 std::to_string(osd_weight.first),
				 std::to_string(osd_weight.second));
      }
      string s = json_spirit::write(json_object);
      std::replace(begin(s), end(s), '\"', '\'');
      const string cmd =
	"{"
	"\"prefix\": \"osd reweightn\", "
	"\"weights\": \"" + s + "\""
	"}";
      auto on_finish = new ReplyOnFinish(cmdctx);
      monc->start_mon_command({cmd}, {},
			      &on_finish->from_mon, &on_finish->outs, on_finish);
      return true;
    }
  } else if (prefix == "osd df") {
    string method;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "output_method", method);
    r = cluster_state.with_pgservice([&](const PGMapStatService& pgservice) {
	return cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	    print_osd_utilization(osdmap, &pgservice, ss,
				  f.get(), method == "tree");
				  
	    cmdctx->odata.append(ss);
	    return 0;
	  });
      });
    cmdctx->reply(r, "");
    return true;
  } else if (prefix == "osd safe-to-destroy") {
    vector<string> ids;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "ids", ids);
    set<int> osds;
    int r;
    cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	r = osdmap.parse_osd_id_list(ids, &osds, &ss);
      });
    if (!r && osds.empty()) {
      ss << "must specify one or more OSDs";
      r = -EINVAL;
    }
    if (r < 0) {
      cmdctx->reply(r, ss);
      return true;
    }
    set<int> active_osds, missing_stats, stored_pgs;
    int affected_pgs = 0;
    cluster_state.with_pgmap([&](const PGMap& pg_map) {
	if (pg_map.num_pg_unknown > 0) {
	  ss << pg_map.num_pg_unknown << " pgs have unknown state; cannot draw"
	     << " any conclusions";
	  r = -EAGAIN;
	  return;
	}
	int num_active_clean = 0;
	for (auto& p : pg_map.num_pg_by_state) {
	  unsigned want = PG_STATE_ACTIVE|PG_STATE_CLEAN;
	  if ((p.first & want) == want) {
	    num_active_clean += p.second;
	  }
	}
	cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	    for (auto osd : osds) {
	      if (!osdmap.exists(osd)) {
		continue;  // clearly safe to destroy
	      }
	      auto q = pg_map.num_pg_by_osd.find(osd);
	      if (q != pg_map.num_pg_by_osd.end()) {
		if (q->second.acting > 0 || q->second.up > 0) {
		  active_osds.insert(osd);
		  affected_pgs += q->second.acting + q->second.up;
		  continue;
		}
	      }
	      if (num_active_clean < pg_map.num_pg) {
		// all pgs aren't active+clean; we need to be careful.
		auto p = pg_map.osd_stat.find(osd);
		if (p == pg_map.osd_stat.end()) {
		  missing_stats.insert(osd);
		}
		if (p->second.num_pgs > 0) {
		  stored_pgs.insert(osd);
		}
	      }
	    }
	  });
      });
    if (!r && !active_osds.empty()) {
      ss << "OSD(s) " << active_osds << " have " << affected_pgs
	 << " pgs currently mapped to them";
      r = -EBUSY;
    } else if (!missing_stats.empty()) {
      ss << "OSD(s) " << missing_stats << " have no reported stats, and not all"
	 << " PGs are active+clean; we cannot draw any conclusions";
      r = -EAGAIN;
    } else if (!stored_pgs.empty()) {
      ss << "OSD(s) " << stored_pgs << " last reported they still store some PG"
	 << " data, and not all PGs are active+clean; we cannot be sure they"
	 << " aren't still needed.";
      r = -EBUSY;
    }
    if (r) {
      cmdctx->reply(r, ss);
      return true;
    }
    ss << "OSD(s) " << osds << " are safe to destroy without reducing data"
       << " durability.";
    cmdctx->reply(0, ss);
    return true;
  } else if (prefix == "osd ok-to-stop") {
    vector<string> ids;
    cmd_getval(g_ceph_context, cmdctx->cmdmap, "ids", ids);
    set<int> osds;
    int r;
    cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	r = osdmap.parse_osd_id_list(ids, &osds, &ss);
      });
    if (!r && osds.empty()) {
      ss << "must specify one or more OSDs";
      r = -EINVAL;
    }
    if (r < 0) {
      cmdctx->reply(r, ss);
      return true;
    }
    map<pg_t,int> pg_delta;  // pgid -> net acting set size change
    int dangerous_pgs = 0;
    cluster_state.with_pgmap([&](const PGMap& pg_map) {
	return cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	    if (pg_map.num_pg_unknown > 0) {
	      ss << pg_map.num_pg_unknown << " pgs have unknown state; "
		 << "cannot draw any conclusions";
	      r = -EAGAIN;
	      return;
	    }
	    for (auto osd : osds) {
	      auto p = pg_map.pg_by_osd.find(osd);
	      if (p != pg_map.pg_by_osd.end()) {
		for (auto& pgid : p->second) {
		  --pg_delta[pgid];
		}
	      }
	    }
	    for (auto& p : pg_delta) {
	      auto q = pg_map.pg_stat.find(p.first);
	      if (q == pg_map.pg_stat.end()) {
		ss << "missing information about " << p.first << "; cannot draw"
		   << " any conclusions";
		r = -EAGAIN;
		return;
	      }
	      if (!(q->second.state & PG_STATE_ACTIVE) ||
		  (q->second.state & PG_STATE_DEGRADED)) {
		// we don't currently have a good way to tell *how* degraded
		// a degraded PG is, so we have to assume we cannot remove
		// any more replicas/shards.
		++dangerous_pgs;
		continue;
	      }
	      const pg_pool_t *pi = osdmap.get_pg_pool(p.first.pool());
	      if (!pi) {
		++dangerous_pgs; // pool is creating or deleting
	      } else {
		if (q->second.acting.size() + p.second < pi->min_size) {
		  ++dangerous_pgs;
		}
	      }
	    }
	  });
      });
    if (r) {
      cmdctx->reply(r, ss);
      return true;
    }
    if (dangerous_pgs) {
      ss << dangerous_pgs << " PGs are already degraded or might become "
	 << "unavailable";
      cmdctx->reply(-EBUSY, ss);
      return true;
    }
    ss << "OSD(s) " << osds << " are ok to stop without reducing"
       << " availability, provided there are no other concurrent failures"
       << " or interventions. " << pg_delta.size() << " PGs are likely to be"
       << " degraded (but remain available) as a result.";
    cmdctx->reply(0, ss);
    return true;
  } else if (prefix == "pg force-recovery" ||
  	       prefix == "pg force-backfill" ||
  	       prefix == "pg cancel-force-recovery" ||
  	       prefix == "pg cancel-force-backfill") {
    string forceop = prefix.substr(3, string::npos);
    list<pg_t> parsed_pgs;
    map<int, list<pg_t> > osdpgs;

    // figure out actual op just once
    int actual_op = 0;
    if (forceop == "force-recovery") {
      actual_op = OFR_RECOVERY;
    } else if (forceop == "force-backfill") {
      actual_op = OFR_BACKFILL;
    } else if (forceop == "cancel-force-backfill") {
      actual_op = OFR_BACKFILL | OFR_CANCEL;
    } else if (forceop == "cancel-force-recovery") {
      actual_op = OFR_RECOVERY | OFR_CANCEL;
    }

    // covnert pg names to pgs, discard any invalid ones while at it
    {
      // we don't want to keep pgidstr and pgidstr_nodup forever
      vector<string> pgidstr;
      // get pgids to process and prune duplicates
      cmd_getval(g_ceph_context, cmdctx->cmdmap, "pgid", pgidstr);
      set<string> pgidstr_nodup(pgidstr.begin(), pgidstr.end());
      if (pgidstr.size() != pgidstr_nodup.size()) {
	// move elements only when there were duplicates, as this
	// reorders them
	pgidstr.resize(pgidstr_nodup.size());
	auto it = pgidstr_nodup.begin();
	for (size_t i = 0 ; i < pgidstr_nodup.size(); i++) {
	  pgidstr[i] = std::move(*it++);
	}
      }

      cluster_state.with_pgmap([&](const PGMap& pg_map) {
	for (auto& pstr : pgidstr) {
	  pg_t parsed_pg;
	  if (!parsed_pg.parse(pstr.c_str())) {
	    ss << "invalid pgid '" << pstr << "'; ";
	    r = -EINVAL;
	  } else {
	    auto workit = pg_map.pg_stat.find(parsed_pg);
	    if (workit == pg_map.pg_stat.end()) {
	      ss << "pg " << pstr << " does not exist; ";
	      r = -ENOENT;
	    } else {
	      pg_stat_t workpg = workit->second;

	      // discard pgs for which user requests are pointless
	      switch (actual_op)
	      {
		case OFR_RECOVERY:
		  if ((workpg.state & (PG_STATE_DEGRADED | PG_STATE_RECOVERY_WAIT | PG_STATE_RECOVERING)) == 0) {
		    // don't return error, user script may be racing with cluster. not fatal.
		    ss << "pg " << pstr << " doesn't require recovery; ";
		    continue;
		  } else  if (workpg.state & PG_STATE_FORCED_RECOVERY) {
		    ss << "pg " << pstr << " recovery already forced; ";
		    // return error, as it may be a bug in user script
		    r = -EINVAL;
		    continue;
		  }
		  break;
		case OFR_BACKFILL:
		  if ((workpg.state & (PG_STATE_DEGRADED | PG_STATE_BACKFILL_WAIT | PG_STATE_BACKFILLING)) == 0) {
		    ss << "pg " << pstr << " doesn't require backfilling; ";
		    continue;
		  } else  if (workpg.state & PG_STATE_FORCED_BACKFILL) {
		    ss << "pg " << pstr << " backfill already forced; ";
		    r = -EINVAL;
		    continue;
		  }
		  break;
		case OFR_BACKFILL | OFR_CANCEL:
		  if ((workpg.state & PG_STATE_FORCED_BACKFILL) == 0) {
		    ss << "pg " << pstr << " backfill not forced; ";
		    continue;
		  }
		  break;
		case OFR_RECOVERY | OFR_CANCEL:
		  if ((workpg.state & PG_STATE_FORCED_RECOVERY) == 0) {
		    ss << "pg " << pstr << " recovery not forced; ";
		    continue;
		  }
		  break;
		default:
		  assert(0 == "actual_op value is not supported");
	      }

	      parsed_pgs.push_back(std::move(parsed_pg));
	    }
	  }
	}

	// group pgs to process by osd
	for (auto& pgid : parsed_pgs) {
	  auto workit = pg_map.pg_stat.find(pgid);
	  if (workit != pg_map.pg_stat.end()) {
	    pg_stat_t workpg = workit->second;
	    set<int32_t> osds(workpg.up.begin(), workpg.up.end());
	    osds.insert(workpg.acting.begin(), workpg.acting.end());
	    for (auto i : osds) {
	      osdpgs[i].push_back(pgid);
	    }
	  }
	}

      });
    }

    // respond with error only when no pgs are correct
    // yes, in case of mixed errors, only the last one will be emitted,
    // but the message presented will be fine
    if (parsed_pgs.size() != 0) {
      // clear error to not confuse users/scripts
      r = 0;
    }

    // optimize the command -> messages conversion, use only one message per distinct OSD
    cluster_state.with_osdmap([&](const OSDMap& osdmap) {
      for (auto& i : osdpgs) {
	if (osdmap.is_up(i.first)) {
	  vector<pg_t> pgvec(make_move_iterator(i.second.begin()), make_move_iterator(i.second.end()));
	  auto p = osd_cons.find(i.first);
	  if (p == osd_cons.end()) {
	    ss << "osd." << i.first << " is not currently connected";
	    r = -EAGAIN;
	    continue;
	  }
	  for (auto& con : p->second) {
	    con->send_message(new MOSDForceRecovery(monc->get_fsid(), pgvec, actual_op));
	  }
	  ss << "instructing pg(s) " << i.second << " on osd." << i.first << " to " << forceop << "; ";
	}
      }
    });
    ss << std::endl;
    cmdctx->reply(r, ss);
    return true;
  } else {
    r = cluster_state.with_pgmap([&](const PGMap& pg_map) {
	return cluster_state.with_osdmap([&](const OSDMap& osdmap) {
	    return process_pg_map_command(prefix, cmdctx->cmdmap, pg_map, osdmap,
					  f.get(), &ss, &cmdctx->odata);
	  });
      });

    if (r != -EOPNOTSUPP) {
      cmdctx->reply(r, ss);
      return true;
    }
  }

  // None of the special native commands, 
  ActivePyModule *handler = nullptr;
  auto py_commands = py_modules.get_py_commands();
  for (const auto &pyc : py_commands) {
    auto pyc_prefix = cmddesc_get_prefix(pyc.cmdstring);
    dout(1) << "pyc_prefix: '" << pyc_prefix << "'" << dendl;
    if (pyc_prefix == prefix) {
      handler = pyc.handler;
      break;
    }
  }

  if (handler == nullptr) {
    ss << "No handler found for '" << prefix << "'";
    dout(4) << "No handler found for '" << prefix << "'" << dendl;
    cmdctx->reply(-EINVAL, ss);
    return true;
  } else {
    // Okay, now we have a handler to call, but we must not call it
    // in this thread, because the python handlers can do anything,
    // including blocking, and including calling back into mgr.
    dout(4) << "passing through " << cmdctx->cmdmap.size() << dendl;
    finisher.queue(new FunctionContext([cmdctx, handler](int r_) {
      std::stringstream ds;
      std::stringstream ss;
      int r = handler->handle_command(cmdctx->cmdmap, &ds, &ss);
      cmdctx->odata.append(ds);
      cmdctx->reply(r, ss);
    }));
    return true;
  }
}