text:
/*
 *  Copyright 2010-2011 Anders Wallin (anders.e.e.wallin "at" gmail.com)
 *
 *  This file is part of OpenCAMlib.
 *
 *  OpenCAMlib is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenCAMlib is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenCAMlib.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <boost/foreach.hpp>

#include "fiber.hpp"

namespace ocl
{

Fiber::Fiber(const Point &p1in, const Point &p2in) {
    p1=p1in;
    p2=p2in;
    calcDir();
}

void Fiber::calcDir() {
    dir = p2 - p1;
    assert( dir.z == 0.0 );
    dir.normalize();
}

bool Fiber::contains(Interval& i) const {
    BOOST_FOREACH( Interval fi, ints) {
        if ( i.inside( fi ) )
            return true;
    }
    return false;
}

bool Fiber::missing(Interval& i) const {
    bool result = true;
    BOOST_FOREACH( Interval fi, ints) {
        if ( !i.outside( fi ) ) // all existing ints must be non-overlapping
            result = false;
    }
    return result;
}

void Fiber::addInterval(Interval& i) {
    if (i.empty())
        return; // do nothing.

    if (ints.empty()) { // empty fiber case
        ints.push_back(i);
        return;
    } else if ( this->contains(i)  ) { // if fiber already contains i
        return; // do nothing
    } else if ( this->missing(i) ) { // if fiber doesn't contain i
        ints.push_back(i);
        return;
    } else {
        // this is the messier general case with partial overlap
        std::vector<Interval>::iterator itr;
        itr = ints.begin();
        std::vector<Interval> overlaps;
        while (itr!=ints.end()) { // loop through all intervals
            if ( ! (itr->outside( i )) ) {
                overlaps.push_back(*itr); // add overlaps here
                itr = ints.erase(itr); // erase overlaps from ints
            } else {
                ++itr;
            }
        }
        overlaps.push_back(i);
        // now build a new interval from i and the overlaps
        Interval sumint;
        BOOST_FOREACH(Interval intr, overlaps) {
            sumint.updateLower( intr.lower, intr.lower_cc );
            sumint.updateUpper( intr.upper, intr.upper_cc );
        }
        ints.push_back(sumint); // add the sum-interval to ints
        return;
    }
}

double Fiber::tval(Point& p) const {
    // fiber is  f = p1 + t * (p2-p1)
    // t = (f-p1).dot(p2-p1) / (p2-p1).dot(p2-p1)
    return  (p-p1).dot(p2-p1) / (p2-p1).dot(p2-p1);
}

Point Fiber::point(double t) const {
    Point p = p1 + t*(p2-p1);
    return p;
}

void Fiber::printInts() const {
    int n=0;
    BOOST_FOREACH( Interval i, ints) {
        std::cout << n << ": [ " << i.lower << " , " << i.upper << " ]" << "\n";
        ++n;
    }
}

std::ostream& operator<<(std::ostream &stream, const Fiber& f) {
  stream << " fiber dir=" << f.dir << " and " << f.ints.size() << " intervals\n";
  stream << " fiber.p1=" << f.p1 << " fiber.p2 " << f.p2 ;
  return stream;
}

} // end namespace
// end file fiber.cpp

max_stars_repo_name: JohnyEngine/CNC

max_issues_repo_name: JohnyEngine/CNC

max_forks_repo_name: JohnyEngine/CNC


text:
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE "rock_replay"
#define BOOST_AUTO_TEST_MAIN

#include <boost/test/unit_test.hpp>

ext: cpp


text: #include <boost/fusion/support/category_of.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
/*
 * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
 */

#include <boost/assign/list_of.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/foreach.hpp>
#include <sandesh/sandesh_types.h>
#include <sandesh/sandesh.h>
#include <sandesh/request_pipeline.h>

#include "base/util.h"
#include "io/tcp_server.h"
#include "control-node/control_node.h"
#include "bgp/bgp_config.h"
#include "bgp/bgp_multicast.h"
#include "bgp/bgp_path.h"
#include "bgp/bgp_peer_types.h"
#include "bgp/bgp_peer_membership.h"
#include "bgp/bgp_route.h"
#include "bgp/bgp_sandesh.h"
#include "bgp/bgp_session_manager.h"
#include "bgp/bgp_table.h"
#include "bgp/bgp_xmpp_channel.h"
#include "bgp/ermvpn/ermvpn_table.h"
#include "bgp/inet/inet_route.h"
#include "bgp/inet/inet_table.h"
#include "bgp/routing-instance/peer_manager.h"
#include "bgp/routing-instance/routing_instance.h"
#include "bgp/origin-vn/origin_vn.h"
#include "bgp/security_group/security_group.h"
#include "bgp/tunnel_encap/tunnel_encap.h"
#include "db/db_table_partition.h"
#include "xmpp/xmpp_connection.h"
#include "xmpp/xmpp_server.h"

using namespace boost::assign;
using namespace std;

struct ShowRouteData : public RequestPipeline::InstData {
    vector<ShowRouteTable> route_table_list;
};

class ShowRouteHandler {
public:
    struct ShowRouteData : public RequestPipeline::InstData {
        vector<ShowRouteTable> route_table_list;
    };

    ShowRouteHandler(const ShowRouteReq *req, int inst_id) :
        req_(req), inst_id_(inst_id) {}

    // Search for interesting prefixes in a given table for specified partition
    void BuildShowRouteTable(BgpTable *table,
            vector<ShowRoute> &route_list,
            int count) {
        DBTablePartition *partition =
            static_cast<DBTablePartition *>(table->GetTablePartition(inst_id_));
        BgpRoute *route;

        bool exact_lookup = false;
        if (!req_->get_prefix().empty() && !req_->get_longer_match()) {
            exact_lookup = true;
            auto_ptr<DBEntry> key = table->AllocEntryStr(req_->get_prefix());
            route = static_cast<BgpRoute *>(partition->Find(key.get()));
        } else if (table->name() == req_->get_start_routing_table()) {
            auto_ptr<DBEntry> key = table->AllocEntryStr(req_->get_start_prefix());
            route = static_cast<BgpRoute *>(partition->lower_bound(key.get()));
        } else {
            route = static_cast<BgpRoute *>(partition->GetFirst());
        }
        for (int i = 0; route && (!count || i < count);
             route = static_cast<BgpRoute *>(partition->GetNext(route)), i++) {
            if (!MatchPrefix(req_->get_prefix(), route,
                             req_->get_longer_match()))
                continue;
            ShowRoute show_route;
            route->FillRouteInfo(table, &show_route);
            route_list.push_back(show_route);
            if (exact_lookup)
                break;
        }
    }

    bool MatchPrefix(const string &expected_prefix, BgpRoute *route,
                     bool longer_match) {
        if (expected_prefix == "") return true;
        if (!longer_match) {
            return expected_prefix == route->ToString();
        }

        // Do longest prefix match.
        return route->IsMoreSpecific(expected_prefix);
    }

    bool match(const string &expected, const string &actual) {
        if (expected == "") return true;
        return expected == actual;
    }

    static RequestPipeline::InstData *CreateData(int stage) {
        return static_cast<RequestPipeline::InstData *>(new ShowRouteData);
    }

    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data);
    static bool CallbackS2(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data);
private:
    const ShowRouteReq *req_;
    int inst_id_;
};

bool ShowRouteHandler::CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
    ShowRouteData* mydata = static_cast<ShowRouteData*>(data);
    int inst_id = ps.stages_[stage].instances_[instNum];
    const ShowRouteReq *req = static_cast<const ShowRouteReq *>(ps.snhRequest_.get());
    ShowRouteHandler handler(req, inst_id);
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(req->client_context());
    RoutingInstanceMgr *rim = bsc->bgp_server->routing_instance_mgr();

    string exact_routing_table = req->get_routing_table();
    string exact_routing_instance;
    string start_routing_instance;
    if (exact_routing_table.empty()) {
        exact_routing_instance = req->get_routing_instance();
    } else {
        exact_routing_instance =
            RoutingInstance::GetVrfFromTableName(exact_routing_table);
    }
    if (exact_routing_instance.empty()) {
        start_routing_instance = req->get_start_routing_instance();
    } else {
        start_routing_instance = exact_routing_instance;
    }

    RoutingInstanceMgr::NameIterator i =
        rim->name_lower_bound(start_routing_instance);
    uint32_t count = 0;
    while (i != rim->name_end()) {
        if (!handler.match(exact_routing_instance, i->first))
            break;
        RoutingInstance::RouteTableList::const_iterator j;
        if (req->get_start_routing_instance() == i->first)
            j = i->second->GetTables().lower_bound(req->get_start_routing_table());
        else
            j = i->second->GetTables().begin();
        for (;j != i->second->GetTables().end(); j++) {
            BgpTable *table = j->second;
            if (!handler.match(req->get_routing_table(), table->name()))
                continue;
            ShowRouteTable srt;
            srt.set_routing_instance(i->first);
            srt.set_routing_table_name(table->name());
            srt.set_deleted(table->IsDeleted());

            // Encode routing-table stats.
            srt.prefixes = table->Size();
            srt.primary_paths = table->GetPrimaryPathCount();
            srt.secondary_paths = table->GetSecondaryPathCount();
            srt.infeasible_paths = table->GetInfeasiblePathCount();
            srt.paths = srt.primary_paths + srt.secondary_paths;

            vector<ShowRoute> route_list;
            handler.BuildShowRouteTable(table, route_list,
                req->get_count() ? req->get_count() - count : 0);
            if (route_list.size()) {
                srt.set_routes(route_list);
                mydata->route_table_list.push_back(srt);
            }
            count += route_list.size();
            if (req->get_count() && count >= req->get_count()) break;
        }
        if (req->get_count() && count >= req->get_count()) break;

        i++;
    }
    return true;
}

bool ShowRoute::operator<(const ShowRoute &rhs) const {
    return prefix < rhs.prefix;
}
bool ShowRouteTable::operator<(const ShowRouteTable &rhs) const {
    if (routing_instance < rhs.routing_instance) return true;
    if (routing_instance == rhs.routing_instance)
        return routing_table_name < rhs.routing_table_name;
    return false;
}

template <class T>
void MergeSort(vector<T> &result, vector<const vector<T> *> &input, int limit);

int MergeValues(ShowRoute &result, vector<const ShowRoute *> &input, int limit) {
    assert(input.size() == 1);
    result = *input[0];
    return 1;
}

int MergeValues(ShowRouteTable &result, vector<const ShowRouteTable *> &input,
                 int limit) {
    vector<const vector<ShowRoute> *> list;
    result.routing_instance = input[0]->routing_instance;
    result.routing_table_name = input[0]->routing_table_name;
    result.deleted = input[0]->deleted;
    result.prefixes = input[0]->prefixes;
    result.primary_paths = input[0]->primary_paths;
    result.secondary_paths = input[0]->secondary_paths;
    result.infeasible_paths = input[0]->infeasible_paths;
    result.paths = input[0]->paths;

    int count = 0;
    for (size_t i = 0; i < input.size(); i++) {
        if (input[i]->routes.size())
            list.push_back(&input[i]->routes);
    }
    MergeSort(result.routes, list, limit ? limit - count : 0);
    count += result.routes.size();
    return count;
}

// Merge n number of vectors in result. input is a vector of pointers to vector
template <class T>
void MergeSort(vector<T> &result, vector<const vector<T> *> &input, int limit) {
    size_t size = input.size();
    size_t index[size];
    bzero(index, sizeof(index));
    int count = 0;
    while (limit == 0 || count < limit) {
        size_t best_index = size;
        const T *best = NULL;
        for (size_t i = 0; i < size; i++) {
            if (index[i] == input[i]->size()) continue;
            if (best == NULL ||
                input[i]->at(index[i]) < *best) {
                best = &input[i]->at(index[i]);
                best_index = i;
                continue;
            }
        }
        if (best_index >= size) break;
        T table;
        vector<const T *> list;
        for (size_t j = best_index; j < size; j++) {
            if (index[j] == input[j]->size()) continue;
            if (input[j]->at(index[j]) < *best ||
                *best < input[j]->at(index[j]))
                continue;
            list.push_back(&input[j]->at(index[j]));
            index[j]++;
        }
        count += MergeValues(table, list, limit ? limit - count : 0);
        result.push_back(table);
    }

}

bool ShowRouteHandler::CallbackS2(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
    const ShowRouteReq *req = static_cast<const ShowRouteReq *>(ps.snhRequest_.get());
    const RequestPipeline::StageData *sd = ps.GetStageData(0);
    ShowRouteResp *resp = new ShowRouteResp;
    vector<ShowRouteTable> route_table_list;
    vector<const vector<ShowRouteTable> *> table_lists;
    for (size_t i = 0; i < sd->size(); i++) {
        const ShowRouteData &old_data = static_cast<const ShowRouteData &>(sd->at(i));
        if (old_data.route_table_list.size())
            table_lists.push_back(&old_data.route_table_list);
    }
    MergeSort(route_table_list, table_lists, req->get_count());
    resp->set_tables(route_table_list);
    resp->set_context(req->context());
    resp->Response();
    return true;
}

// handler for 'show route'
void ShowRouteReq::HandleRequest() const {
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(client_context());

    RequestPipeline::PipeSpec ps(this);

    // Request pipeline has 2 stages. In first stage, we spawn one task per
    // partition and generate the list of routes. In second stage, we look
    // at the generated list and merge it so that we can send it out
    //
    // In future, we can enhance it to have many stages and send partial output
    // after every second stage
    RequestPipeline::StageSpec s1, s2;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    s1.taskId_ = scheduler->GetTaskId("db::DBTable");
    s1.allocFn_ = ShowRouteHandler::CreateData;

    s1.cbFn_ = ShowRouteHandler::CallbackS1;
    for (int i = 0; i < bsc->bgp_server->database()->PartitionCount(); i++) {
        s1.instances_.push_back(i);
    }

    s2.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s2.allocFn_ = ShowRouteHandler::CreateData;
    s2.cbFn_ = ShowRouteHandler::CallbackS2;
    s2.instances_.push_back(0);

    ps.stages_= list_of(s1)(s2);
    RequestPipeline rp(ps);
}

class ShowNeighborHandler {
public:
    struct ShowNeighborData : public RequestPipeline::InstData {
        vector<BgpNeighborResp> nbr_list;
    };
    struct ShowNeighborDataS2Key {
        string peer;
        string table;
        bool operator<(const ShowNeighborDataS2Key&rhs) const {
            if (peer != rhs.peer) return (peer < rhs.peer);
            return (table < rhs.table);
        }
    };
    struct ShowNeighborDataS2 : public RequestPipeline::InstData {
        typedef map<ShowNeighborDataS2Key, BgpNeighborRoutingTable> Map;
        Map peers;
    };
    static RequestPipeline::InstData *CreateData(int stage) {
        switch (stage) {
        case 0:
        case 2:
            return static_cast<RequestPipeline::InstData *>(new ShowNeighborData);
        case 1:
            return static_cast<RequestPipeline::InstData *>(new ShowNeighborDataS2);
        default:
            return NULL;
        }
    }
    static void FillNeighborStats(ShowNeighborDataS2 *data, BgpTable *table, int inst_id) {
        DBTablePartition *partition =
            static_cast<DBTablePartition *>(table->GetTablePartition(inst_id));
        BgpRoute *route = static_cast<BgpRoute *>(partition->GetFirst());
        ShowNeighborDataS2Key key;
        key.table = table->name();
        for (; route; route = static_cast<BgpRoute *>(partition->GetNext(route))) {
            Route::PathList &plist = route->GetPathList();
            Route::PathList::iterator it = plist.begin();
            for (; it != plist.end(); it++) {
                BgpPath *path = static_cast<BgpPath *>(it.operator->());
                // If paths are added with Peer as NULL(e.g. aggregate route)
                if (path->GetPeer() == NULL) continue;
                key.peer = path->GetPeer()->ToString();
                BgpNeighborRoutingTable &nt = data->peers[key];
                nt.received_prefixes++;
                if (path->IsFeasible()) nt.accepted_prefixes++;
                if (route->BestPath()->GetPeer() == path->GetPeer()) nt.active_prefixes++;
            }
        }
    }
    static void FillXmppNeighborInfo(vector<BgpNeighborResp> *, BgpServer *, BgpXmppChannel *);
    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data);
    static bool CallbackS2(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data);
    static bool CallbackS3(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data);
};

void ShowNeighborHandler::FillXmppNeighborInfo(
        vector<BgpNeighborResp> *nbr_list, BgpServer *bgp_server, BgpXmppChannel *bx_channel) {
    BgpNeighborResp resp;
    resp.set_peer(bx_channel->ToString());
    resp.set_peer_address(bx_channel->remote_endpoint().address().to_string());
    resp.set_deleted(bx_channel->peer_deleted());
    resp.set_local_address(bx_channel->local_endpoint().address().to_string());
    resp.set_peer_type("internal");
    resp.set_encoding("XMPP");
    resp.set_state(bx_channel->StateName());

    const XmppConnection *connection = bx_channel->channel()->connection();
    resp.set_configured_hold_time(connection->GetConfiguredHoldTime());
    resp.set_negotiated_hold_time(connection->GetNegotiatedHoldTime());

    PeerRibMembershipManager *mgr =
        bx_channel->Peer()->server()->membership_mgr();
    mgr->FillPeerMembershipInfo(bx_channel->Peer(), resp);
    bx_channel->FillTableMembershipInfo(&resp);
    bx_channel->FillInstanceMembershipInfo(&resp);

    BgpPeer::FillBgpNeighborDebugState(resp, bx_channel->Peer()->peer_stats());
    nbr_list->push_back(resp);
}

bool ShowNeighborHandler::CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData * data) {
    ShowNeighborData* mydata = static_cast<ShowNeighborData*>(data);
    const BgpNeighborReq *req = static_cast<const BgpNeighborReq *>(ps.snhRequest_.get());
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(req->client_context());
    RoutingInstanceMgr *rim = bsc->bgp_server->routing_instance_mgr();
    if (req->get_domain() != "") {
        RoutingInstance *ri = rim->GetRoutingInstance(req->get_domain());
        if (ri)
            ri->peer_manager()->FillBgpNeighborInfo(mydata->nbr_list,
                req->get_ip_address());
    } else {
        RoutingInstanceMgr::RoutingInstanceIterator it = rim->begin();
        for (;it != rim->end(); it++) {
            it->peer_manager()->FillBgpNeighborInfo(mydata->nbr_list,
                req->get_ip_address());
        }
    }

    bsc->xmpp_peer_manager->VisitChannels(boost::bind(FillXmppNeighborInfo,
                                                      &mydata->nbr_list, bsc->bgp_server, _1));

    return true;
}

bool ShowNeighborHandler::CallbackS2(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
    ShowNeighborDataS2* mydata = static_cast<ShowNeighborDataS2*>(data);
    int inst_id = ps.stages_[stage].instances_[instNum];
    const BgpNeighborReq *req = static_cast<const BgpNeighborReq *>(ps.snhRequest_.get());
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(req->client_context());
    RoutingInstanceMgr *rim = bsc->bgp_server->routing_instance_mgr();
    RoutingInstanceMgr::NameIterator i = rim->name_begin();
    for (;i != rim->name_end(); i++) {
        RoutingInstance::RouteTableList::const_iterator j =
                i->second->GetTables().begin();
        for (;j != i->second->GetTables().end(); j++) {
            FillNeighborStats(mydata, j->second, inst_id);
        }
    }
    return true;
}

bool ShowNeighborHandler::CallbackS3(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
    const BgpNeighborReq *req = static_cast<const BgpNeighborReq *>(ps.snhRequest_.get());
    const RequestPipeline::StageData *sd[2] = { ps.GetStageData(0), ps.GetStageData(1) };
    const ShowNeighborData &nbrs =  static_cast<const ShowNeighborData &>(sd[0]->at(0));

    vector<BgpNeighborResp> nbr_list = nbrs.nbr_list;

    for (size_t i = 0; i < nbrs.nbr_list.size(); i++) {
        ShowNeighborDataS2Key key;
        key.peer = nbrs.nbr_list[i].peer;
        for (size_t j = 0; j < nbrs.nbr_list[i].routing_tables.size(); j++) {
            key.table = nbrs.nbr_list[i].routing_tables[j].name;
            for (size_t k = 0; k < sd[1]->size(); k++) {
                const ShowNeighborDataS2 &stats_data =
                        static_cast<const ShowNeighborDataS2 &>(sd[1]->at(k));
                ShowNeighborDataS2::Map::const_iterator it = stats_data.peers.find(key);
                if (it == stats_data.peers.end()) continue;
                nbr_list[i].routing_tables[j].active_prefixes +=
                        it->second.active_prefixes;
                nbr_list[i].routing_tables[j].received_prefixes +=
                        it->second.received_prefixes;
                nbr_list[i].routing_tables[j].accepted_prefixes +=
                        it->second.accepted_prefixes;
            }
        }
    }

    BgpNeighborListResp *resp = new BgpNeighborListResp;
    resp->set_neighbors(nbrs.nbr_list);
    resp->set_context(req->context());
    resp->Response();
    return true;
}

class ShowNeighborStatisticsHandler {
public:
    static void FillXmppNeighborStatistics(size_t *count, BgpServer *bgp_server,
                                           string domain, string up_or_down,
                                           BgpXmppChannel *channel);
    static bool CallbackS1(const Sandesh *sr,
                           const RequestPipeline::PipeSpec ps, int stage,
                           int instNum, RequestPipeline::InstData * data);
    static size_t FillBgpNeighborStatistics(const ShowNeighborStatisticsReq *req,
                                   BgpServer *bgp_server);
};

void ShowNeighborStatisticsHandler::FillXmppNeighborStatistics(
        size_t *count, BgpServer *bgp_server, string domain,
        string up_or_down, BgpXmppChannel *channel) {

    if (boost::iequals(up_or_down, "up") && !channel->Peer()->IsReady()) {
        return;
    }
    if (boost::iequals(up_or_down, "down") && channel->Peer()->IsReady()) {
        return;
    }

    if (!domain.empty()) {
        RoutingInstanceMgr *rim = bgp_server->routing_instance_mgr();
        RoutingInstance *ri = rim->GetRoutingInstance(domain);
        if (!ri) return;
        BgpTable *table = ri->GetTable(Address::INET);
        if (!table) return;

        if (!bgp_server->membership_mgr()->IPeerRibFind(channel->Peer(),
                                                        table)) {
            return;
        }
    }

    ++*count;
}

size_t ShowNeighborStatisticsHandler::FillBgpNeighborStatistics(
           const ShowNeighborStatisticsReq *req, BgpServer *bgp_server) {
    size_t count = 0;

    if (req->get_bgp_or_xmpp().empty() ||
            boost::iequals(req->get_bgp_or_xmpp(), "bgp")) {
        RoutingInstanceMgr *rim = bgp_server->routing_instance_mgr();
        if (!req->get_domain().empty()) {
            RoutingInstance *ri = rim->GetRoutingInstance(req->get_domain());
            if (ri) {
                count += ri->peer_manager()->GetNeighborCount(req->get_up_or_down());
            }
        } else {
            RoutingInstanceMgr::RoutingInstanceIterator it = rim->begin();
            for (;it != rim->end(); it++) {
                count += it->peer_manager()->GetNeighborCount(req->get_up_or_down());
            }
        }
    }

    return count;
}

bool ShowNeighborStatisticsHandler::CallbackS1(
        const Sandesh *sr, const RequestPipeline::PipeSpec ps,
        int stage, int instNum, RequestPipeline::InstData *data) {

    const ShowNeighborStatisticsReq *req;
    BgpSandeshContext *bsc;

    req = static_cast<const ShowNeighborStatisticsReq *>(
                                ps.snhRequest_.get());
    bsc = static_cast<BgpSandeshContext *>(req->client_context());

    // Retrieve number of BGP peers.
    size_t count = FillBgpNeighborStatistics(req, bsc->bgp_server);

    // Retrieve numner of XMPP agents.
    if (req->get_bgp_or_xmpp().empty() ||
            boost::iequals(req->get_bgp_or_xmpp(), "xmpp")) {
        bsc->xmpp_peer_manager->VisitChannels(
                boost::bind(FillXmppNeighborStatistics, &count,
                            bsc->bgp_server, req->get_domain(),
                            req->get_up_or_down(), _1));
    }

    ShowNeighborStatisticsResp *resp = new ShowNeighborStatisticsResp;
    resp->set_bgp_or_xmpp(req->get_bgp_or_xmpp());
    resp->set_up_or_down(req->get_up_or_down());
    resp->set_domain(req->get_domain());
    resp->set_count(count);
    resp->set_context(req->context());
    resp->Response();

    return true;
}

void ShowNeighborStatisticsReq::HandleRequest() const {

    // Use config task as we need to examine both bgp and xmpp data bases
    // to compute the number of neighbors. Both BGP and XMPP peers are
    // inserted/deleted under config task.
    RequestPipeline::StageSpec s1;
    s1.taskId_ = TaskScheduler::GetInstance()->GetTaskId("bgp::Config");
    s1.instances_.push_back(0);
    s1.cbFn_ = ShowNeighborStatisticsHandler::CallbackS1;

    RequestPipeline::PipeSpec ps(this);
    ps.stages_= list_of(s1);
    RequestPipeline rp(ps);
}

// handler for 'show bgp neighbor'
void BgpNeighborReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(client_context());

    // Request pipeline has 3 stages:
    // First stage to collect neighbor info
    // Second stage to collect stats
    // Third stage to collate all data and respond to the request
    RequestPipeline::StageSpec s1, s2, s3;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    s1.taskId_ = scheduler->GetTaskId("bgp::PeerMembership");
    s1.allocFn_ = ShowNeighborHandler::CreateData;
    s1.cbFn_ = ShowNeighborHandler::CallbackS1;
    s1.instances_.push_back(
            PeerRibMembershipManager::kMembershipTaskInstanceId);

    s2.taskId_ = scheduler->GetTaskId("db::DBTable");
    s2.allocFn_ = ShowNeighborHandler::CreateData;
    s2.cbFn_ = ShowNeighborHandler::CallbackS2;
    for (int i = 0; i < bsc->bgp_server->database()->PartitionCount(); i++) {
        s2.instances_.push_back(i);
    }

    s3.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s3.allocFn_ = ShowNeighborHandler::CreateData;
    s3.cbFn_ = ShowNeighborHandler::CallbackS3;
    s3.instances_.push_back(0);
    ps.stages_= list_of(s1)(s2)(s3);
    RequestPipeline rp(ps);

}

class ClearBgpNeighborHandler {
public:
    static bool CallbackS1(const Sandesh *sr,
                           const RequestPipeline::PipeSpec ps, int stage,
                           int instNum, RequestPipeline::InstData * data);
};

bool ClearBgpNeighborHandler::CallbackS1(
        const Sandesh *sr, const RequestPipeline::PipeSpec ps,
        int stage, int instNum, RequestPipeline::InstData *data) {
    const ClearBgpNeighborReq *req;
    BgpSandeshContext *bsc;

    req = static_cast<const ClearBgpNeighborReq *>(ps.snhRequest_.get());
    bsc = static_cast<BgpSandeshContext *>(req->client_context());
    BgpPeer *peer = bsc->bgp_server->FindPeer(req->get_name());

    ClearBgpNeighborResp *resp = new ClearBgpNeighborResp;
    if (peer) {
        peer->Clear(BgpProto::Notification::AdminReset);
        resp->set_success(true);
    } else {
        resp->set_success(false);
    }
    resp->set_context(req->context());
    resp->Response();

    return true;
}

// handler for 'clear bgp neighbor'
void ClearBgpNeighborReq::HandleRequest() const {

    if (ControlNode::GetTestMode() == false) {
        ClearBgpNeighborResp *resp = new ClearBgpNeighborResp;
        resp->set_context(context());
        resp->set_more(false);
        resp->Response();
        return;
    }

    // Use config task since neighbors are added/deleted under that task.
    // to compute the number of neighbors.
    RequestPipeline::StageSpec s1;
    s1.taskId_ = TaskScheduler::GetInstance()->GetTaskId("bgp::Config");
    s1.instances_.push_back(0);
    s1.cbFn_ = ClearBgpNeighborHandler::CallbackS1;

    RequestPipeline::PipeSpec ps(this);
    ps.stages_= list_of(s1);
    RequestPipeline rp(ps);
}

class ShowRoutingInstanceHandler {
public:
    static void FillRoutingTableStats(ShowRoutingInstanceTable &rit,
                                      BgpTable *table) {
        rit.set_name(table->name());
        rit.set_walk_requests(
            table->database()->GetWalker()->walk_request_count());
        rit.set_walk_completes(
            table->database()->GetWalker()->walk_complete_count());
        rit.set_walk_cancels(
            table->database()->GetWalker()->walk_cancel_count());
        size_t markers;
        rit.set_pending_updates(table->GetPendingRiboutsCount(markers));
        rit.set_markers(markers);
        rit.prefixes = table->Size();
        rit.primary_paths = table->GetPrimaryPathCount();
        rit.secondary_paths = table->GetSecondaryPathCount();
        rit.infeasible_paths = table->GetInfeasiblePathCount();
        rit.paths = rit.primary_paths + rit.secondary_paths;
    }

    static void FillRoutingInstanceInfo(const RequestPipeline::StageData *sd,
            vector<ShowRoutingInstance> &ri_list,
            RoutingInstance *ri, PeerRibMembershipManager *pmm) {
        const RoutingInstance::RouteTableList &tables = ri->GetTables();
        RoutingInstance::RouteTableList::const_iterator it = tables.begin();
        ShowRoutingInstance inst;
        vector<ShowRoutingInstanceTable> rit_list;
        for (; it != tables.end(); it++) {
            ShowRoutingInstanceTable table;
            pmm->FillRoutingInstanceInfo(table, it->second);
            FillRoutingTableStats(table, it->second);
            rit_list.push_back(table);
        }
        if (rit_list.size()) {
            inst.set_name(ri->name());
            inst.set_virtual_network(ri->virtual_network());
            inst.set_vn_index(ri->virtual_network_index());
            inst.set_deleted(ri->deleted());
            std::vector<std::string> import_rt;
            BOOST_FOREACH(RouteTarget rt, ri->GetImportList()) {
                import_rt.push_back(rt.ToString());
            }
            inst.set_import_target(import_rt);

            std::vector<std::string> export_rt;
            BOOST_FOREACH(RouteTarget rt, ri->GetExportList()) {
                export_rt.push_back(rt.ToString());
            }
            inst.set_export_target(export_rt);
            inst.set_tables(rit_list);
            ri_list.push_back(inst);
        }
    }

    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowRoutingInstanceReq *req =
            static_cast<const ShowRoutingInstanceReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(
                                                 req->client_context());
        RoutingInstanceMgr *rim = bsc->bgp_server->routing_instance_mgr();
        PeerRibMembershipManager *pmm = bsc->bgp_server->membership_mgr();
        vector<ShowRoutingInstance> ri_list;
        const RequestPipeline::StageData *sd = ps.GetStageData(0);
        if (req->get_name() != "") {
            RoutingInstance *ri = rim->GetRoutingInstance(req->get_name());
            if (ri)
                FillRoutingInstanceInfo(sd, ri_list, ri, pmm);
        } else {
            RoutingInstanceMgr::RoutingInstanceIterator it = rim->begin();
            for (;it != rim->end(); it++) {
                FillRoutingInstanceInfo(sd, ri_list, &(*it), pmm);
            }
        }

        ShowRoutingInstanceResp *resp = new ShowRoutingInstanceResp;
        resp->set_instances(ri_list);
        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowRoutingInstanceReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);

    // Request pipeline has 2 stages:
    // First stage to collect routing instance stats
    // Second stage to collect peer info and fill stats from stage 1 and
    // respond to the request
    RequestPipeline::StageSpec s1;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();

    s1.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s1.cbFn_ = ShowRoutingInstanceHandler::CallbackS1;
    s1.instances_.push_back(0);
    ps.stages_ = list_of(s1);
    RequestPipeline rp(ps);
}

class ShowMulticastManagerHandler {
public:
    struct MulticastManagerDataKey {
        string routing_table;
        bool operator<(const MulticastManagerDataKey &rhs) const {
            return (routing_table < rhs.routing_table);
        }
    };

    struct MulticastManagerData : public RequestPipeline::InstData {
        typedef map<MulticastManagerDataKey, uint32_t> Map;
        Map table_map;
    };

    static RequestPipeline::InstData *CreateData(int stage) {
        return (new MulticastManagerData);
    }

    static void FillMulticastManagerStats(MulticastManagerData *data,
            ErmVpnTable *table, int inst_id) {
        MulticastManagerDataKey key;
        key.routing_table = table->name();
        McastTreeManager *tm = table->GetTreeManager();
        McastManagerPartition *partition = tm->GetPartition(inst_id);
        data->table_map[key] = partition->size();
    }

    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        int inst_id = ps.stages_[stage].instances_[instNum];

        MulticastManagerData *mydata =
            static_cast<MulticastManagerData *>(data);
        const ShowMulticastManagerReq *req =
            static_cast<const ShowMulticastManagerReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        RoutingInstanceMgr *rim = bsc->bgp_server->routing_instance_mgr();
        for (RoutingInstanceMgr::NameIterator it = rim->name_begin();
             it != rim->name_end(); it++) {
            RoutingInstance *ri = it->second;
            ErmVpnTable *table =
                static_cast<ErmVpnTable *>(ri->GetTable(Address::ERMVPN));
            if (table)
                FillMulticastManagerStats(mydata, table, inst_id);
        }

        return true;
    }

    static void FillMulticastManagerInfo(const RequestPipeline::StageData *sd,
            vector<ShowMulticastManager> &mgr_list, ErmVpnTable *table) {
        ShowMulticastManager mgr;
        MulticastManagerDataKey key;
        key.routing_table = table->name();
        for (size_t idx = 0; idx < sd->size(); idx++) {
            const MulticastManagerData &data =
                static_cast<const MulticastManagerData &>(sd->at(idx));
            MulticastManagerData::Map::const_iterator dit =
                data.table_map.find(key);
            if (dit != data.table_map.end()) {
                mgr.total_trees += dit->second;
            }
        }

        mgr.set_name(table->name());
        mgr_list.push_back(mgr);
    }

    static bool CallbackS2(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowMulticastManagerReq *req =
            static_cast<const ShowMulticastManagerReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        RoutingInstanceMgr *rim = bsc->bgp_server->routing_instance_mgr();
        vector<ShowMulticastManager> mgr_list;
        const RequestPipeline::StageData *sd = ps.GetStageData(0);
        for (RoutingInstanceMgr::NameIterator it = rim->name_begin();
             it != rim->name_end(); it++) {
            RoutingInstance *ri = it->second;
            ErmVpnTable *table =
                static_cast<ErmVpnTable *>(ri->GetTable(Address::ERMVPN));
            if (table)
                FillMulticastManagerInfo(sd, mgr_list, table);
        }

        ShowMulticastManagerResp *resp = new ShowMulticastManagerResp;
        resp->set_managers(mgr_list);
        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowMulticastManagerReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(client_context());

    // Request pipeline has 2 stages.
    // First stage to collect multicast manager stats.
    // Second stage to fill stats from stage 1 and respond to the request.
    RequestPipeline::StageSpec s1, s2;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();

    s1.taskId_ = scheduler->GetTaskId("db::DBTable");
    s1.allocFn_ = ShowMulticastManagerHandler::CreateData;
    s1.cbFn_ = ShowMulticastManagerHandler::CallbackS1;
    for (int i = 0; i < bsc->bgp_server->database()->PartitionCount(); i++) {
        s1.instances_.push_back(i);
    }

    s2.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s2.cbFn_ = ShowMulticastManagerHandler::CallbackS2;
    s2.instances_.push_back(0);

    ps.stages_ = list_of(s1)(s2);
    RequestPipeline rp(ps);
}

class ShowMulticastManagerDetailHandler {
public:
    struct MulticastManagerDetailData : public RequestPipeline::InstData {
        vector<ShowMulticastTree> tree_list;
    };

    static RequestPipeline::InstData *CreateData(int stage) {
        return (new MulticastManagerDetailData);
    }

    static void FillMulticastLinkInfo(ShowMulticastForwarder *fwd,
            const McastForwarder *forwarder) {
        ShowMulticastTreeLink link;
        link.set_address(forwarder->address().to_string());
        link.set_label(forwarder->label());
        fwd->links.push_back(link);
    }

    static void FillMulticastForwarderInfo(ShowMulticastTree *tree,
            const McastForwarder *forwarder) {
        ShowMulticastForwarder fwd;
        fwd.set_address(forwarder->address().to_string());
        fwd.set_label(forwarder->label());
        for (McastForwarderList::const_iterator it =
             forwarder->tree_links_.begin();
             it != forwarder->tree_links_.end(); it++) {
            FillMulticastLinkInfo(&fwd, *it);
        }
        tree->forwarders.push_back(fwd);
    }

    static void FillMulticastTreeInfo(MulticastManagerDetailData *data,
            const McastSGEntry *sg) {
        ShowMulticastTree tree;
        tree.set_group(sg->group().to_string());
        tree.set_source(sg->source().to_string());
        for (McastSGEntry::ForwarderSet::const_iterator it =
             sg->forwarder_sets_[0]->begin();
             it != sg->forwarder_sets_[0]->end(); it++) {
            FillMulticastForwarderInfo(&tree, *it);
        }
        data->tree_list.push_back(tree);
    }

    static void FillMulticastPartitionInfo(MulticastManagerDetailData *data,
            ErmVpnTable *table, int inst_id) {
        McastTreeManager *tm = table->GetTreeManager();
        McastManagerPartition *partition = tm->GetPartition(inst_id);
        for (McastManagerPartition::SGList::const_iterator it =
             partition->sg_list_.begin();
             it != partition->sg_list_.end(); it++) {
            FillMulticastTreeInfo(data, *it);
        }
    }

    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        int inst_id = ps.stages_[stage].instances_[instNum];

        MulticastManagerDetailData *mydata =
            static_cast<MulticastManagerDetailData *>(data);
        const ShowMulticastManagerDetailReq *req =
            static_cast<const ShowMulticastManagerDetailReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        DBTableBase *table = bsc->bgp_server->database()->FindTable(req->get_name());
        ErmVpnTable *mcast_table = dynamic_cast<ErmVpnTable *>(table);
        if (mcast_table)
            FillMulticastPartitionInfo(mydata, mcast_table, inst_id);

        return true;
    }

    static void CombineMulticastPartitionInfo(
            const RequestPipeline::StageData *sd,
            vector<ShowMulticastTree> &tree_list) {
        for (size_t idx = 0; idx < sd->size(); idx++) {
            const MulticastManagerDetailData &data =
                static_cast<const MulticastManagerDetailData &>(sd->at(idx));
            tree_list.insert(tree_list.end(),
                             data.tree_list.begin(), data.tree_list.end());
        }
    }

    static bool CallbackS2(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowMulticastManagerReq *req =
            static_cast<const ShowMulticastManagerReq *>(ps.snhRequest_.get());
        const RequestPipeline::StageData *sd = ps.GetStageData(0);
        vector<ShowMulticastTree> tree_list;
        CombineMulticastPartitionInfo(sd, tree_list);

        ShowMulticastManagerDetailResp *resp = new ShowMulticastManagerDetailResp;
        resp->set_trees(tree_list);
        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowMulticastManagerDetailReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(client_context());

    // Request pipeline has 2 stages.
    // First stage to collect multicast manager stats.
    // Second stage to fill stats from stage 1 and respond to the request.
    RequestPipeline::StageSpec s1, s2;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();

    s1.taskId_ = scheduler->GetTaskId("db::DBTable");
    s1.allocFn_ = ShowMulticastManagerDetailHandler::CreateData;
    s1.cbFn_ = ShowMulticastManagerDetailHandler::CallbackS1;
    for (int i = 0; i < bsc->bgp_server->database()->PartitionCount(); i++) {
        s1.instances_.push_back(i);
    }

    s2.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s2.cbFn_ = ShowMulticastManagerDetailHandler::CallbackS2;
    s2.instances_.push_back(0);

    ps.stages_ = list_of(s1)(s2);
    RequestPipeline rp(ps);
}

class ShowRouteVrfHandler {
public:
    struct SearchRouteInVrfData : public RequestPipeline::InstData {
        vector<ShowRoute> routes;
    };

    static RequestPipeline::InstData *CreateData(int stage) {
        return (new SearchRouteInVrfData);
    }

    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        SearchRouteInVrfData *sdata = static_cast<SearchRouteInVrfData *>(data);
        const ShowRouteVrfReq *req =
            static_cast<const ShowRouteVrfReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        RoutingInstanceMgr *rim = bsc->bgp_server->routing_instance_mgr();
        RoutingInstance *ri = rim->GetRoutingInstance(req->get_vrf());
        if (!ri) return true;
        BgpTable *table = ri->GetTable(Address::INET);
        if (!table) return true;
        Ip4Prefix prefix(Ip4Prefix::FromString(req->get_prefix()));
        InetTable::RequestKey key(prefix, NULL);
        InetRoute *route = static_cast<InetRoute *>(table->Find(&key));
        if (!route) return true;
        ShowRoute show_route;
        show_route.set_prefix(route->ToString());
        show_route.set_last_modified(duration_usecs_to_string(
                       UTCTimestampUsec() - route->last_change_at()));
        vector<ShowRoutePath> show_route_paths;
        for(Route::PathList::const_iterator it = route->GetPathList().begin();
            it != route->GetPathList().end(); it++) {
            const BgpPath *path = static_cast<const BgpPath *>(it.operator->());
            ShowRoutePath srp;
            srp.set_protocol("BGP");
            const BgpAttr *attr = path->GetAttr();
            if (attr->as_path() != NULL)
                srp.set_as_path(attr->as_path()->path().ToString());
            srp.set_local_preference(attr->local_pref());
            if (path->GetPeer()) srp.set_source(path->GetPeer()->ToString());
            srp.set_last_modified(duration_usecs_to_string(
                    UTCTimestampUsec() - path->time_stamp_usecs()));
            srp.set_next_hop(attr->nexthop().to_string());
            srp.set_label(path->GetLabel());
            show_route_paths.push_back(srp);
        }
        show_route.set_paths(show_route_paths);
        sdata->routes.push_back(show_route);
        return true;
    }

    static bool CallbackS2(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        const RequestPipeline::StageData *sd = ps.GetStageData(0);
        ShowRoute route;
        for (size_t i = 0; i < sd->size(); i++) {
            const SearchRouteInVrfData &data = static_cast<const SearchRouteInVrfData &>(sd->at(i));
            if (data.routes.size()) {
                route = data.routes.front();
                break;
            }
        }

        ShowRouteVrfResp *resp = new ShowRouteVrfResp;
        resp->set_route(route);
        const ShowRouteVrfReq *req =
            static_cast<const ShowRouteVrfReq *>(ps.snhRequest_.get());
        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowRouteVrfReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);
    BgpSandeshContext *bsc = static_cast<BgpSandeshContext *>(client_context());

    // Request pipeline has 2 stages.
    // First stage to search in different partition
    // Second stage to fill stats from stage 1 and respond to the request.
    RequestPipeline::StageSpec s1, s2;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();

    s1.taskId_ = scheduler->GetTaskId("db::DBTable");
    s1.allocFn_ = ShowRouteVrfHandler::CreateData;
    s1.cbFn_ = ShowRouteVrfHandler::CallbackS1;
    for (int i = 0; i < bsc->bgp_server->database()->PartitionCount(); i++) {
        s1.instances_.push_back(i);
    }

    s2.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s2.cbFn_ = ShowRouteVrfHandler::CallbackS2;
    s2.instances_.push_back(0);

    ps.stages_ = list_of(s1)(s2);
    RequestPipeline rp(ps);
}

class ShowBgpInstanceConfigHandler {
public:
    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowBgpInstanceConfigReq *req =
            static_cast<const ShowBgpInstanceConfigReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        BgpConfigManager *bcm = bsc->bgp_server->config_manager();
        const BgpConfigData &bcd = bcm->config();

        vector<ShowBgpInstanceConfig> ri_list;
        for (BgpConfigData::BgpInstanceMap::const_iterator loc = bcd.instances().begin();
             loc != bcd.instances().end(); ++loc) {
            ShowBgpInstanceConfig inst;
            inst.set_name(loc->second->name());
            inst.set_virtual_network(loc->second->virtual_network());
            inst.set_virtual_network_index(loc->second->virtual_network_index());

            std::vector<std::string> import_list;
            BOOST_FOREACH(std::string rt, loc->second->import_list()) {
                import_list.push_back(rt);
            }
            inst.set_import_target(import_list);

            std::vector<std::string> export_list;
            BOOST_FOREACH(std::string rt, loc->second->export_list()) {
                export_list.push_back(rt);
            }
            inst.set_export_target(export_list);

            const autogen::RoutingInstance *rti = loc->second->instance_config();
            if (rti && rti->IsPropertySet(autogen::RoutingInstance::SERVICE_CHAIN_INFORMATION)) {
                const autogen::ServiceChainInfo &sci = rti->service_chain_information();
                ShowBgpServiceChainConfig scc;
                scc.set_routing_instance(sci.routing_instance);
                scc.set_service_instance(sci.service_instance);
                scc.set_chain_address(sci.service_chain_address);
                scc.set_prefixes(sci.prefix);
                inst.set_service_chain_info(scc);
            }
            if (rti && rti->IsPropertySet(autogen::RoutingInstance::STATIC_ROUTE_ENTRIES)) {
                std::vector<ShowBgpStaticRouteConfig> static_route_list;
                const std::vector<autogen::StaticRouteType> &cfg_list =
                    rti->static_route_entries();
                for (std::vector<autogen::StaticRouteType>::const_iterator
                     static_it = cfg_list.begin(); static_it != cfg_list.end();
                     static_it++) {
                    ShowBgpStaticRouteConfig src;
                    src.set_prefix(static_it->prefix);
                    src.set_targets(static_it->route_target);
                    src.set_nexthop(static_it->prefix);
                    static_route_list.push_back(src);
                }
                inst.set_static_routes(static_route_list);
            }

            ri_list.push_back(inst);
        }

        ShowBgpInstanceConfigResp *resp = new ShowBgpInstanceConfigResp;
        resp->set_instances(ri_list);
        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowBgpInstanceConfigReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);

    // Request pipeline has single stage to collect instance config info
    // and respond to the request
    RequestPipeline::StageSpec s1;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    s1.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s1.cbFn_ = ShowBgpInstanceConfigHandler::CallbackS1;
    s1.instances_.push_back(0);
    ps.stages_ = list_of(s1);
    RequestPipeline rp(ps);
}

class ShowBgpPeeringConfigHandler {
public:
    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowBgpPeeringConfigReq *req =
            static_cast<const ShowBgpPeeringConfigReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        BgpConfigManager *bcm = bsc->bgp_server->config_manager();
        const BgpConfigData &bcd = bcm->config();

        vector<ShowBgpPeeringConfig> peering_list;
        for (BgpConfigData::BgpPeeringMap::const_iterator loc = bcd.peerings().begin();
             loc != bcd.peerings().end(); ++loc) {

            ShowBgpPeeringConfig peering;
            peering.set_instance_name(loc->second->instance()->name());
            peering.set_name(loc->second->name());
            peering.set_neighbor_count(loc->second->size());

            if (!loc->second->bgp_peering()) {
                peering_list.push_back(peering);
                continue;
            }

            vector<ShowBgpSessionConfig> session_list;
            const autogen::BgpPeeringAttributes &attr = loc->second->bgp_peering()->data();
            for (autogen::BgpPeeringAttributes::const_iterator iter1 = attr.begin();
                 iter1 != attr.end(); ++iter1) {
                ShowBgpSessionConfig session;
                session.set_uuid(iter1->uuid);

                vector<ShowBgpSessionAttributesConfig> attribute_list;
                for (std::vector<autogen::BgpSessionAttributes>::const_iterator iter2 =
                     iter1->attributes.begin();
                     iter2 != iter1->attributes.end(); ++iter2) {
                    ShowBgpSessionAttributesConfig attribute;

                    attribute.set_bgp_router(iter2->bgp_router);
                    attribute.set_address_families(iter2->address_families.family);
                    attribute_list.push_back(attribute);
                }

                session.set_attributes(attribute_list);
                session_list.push_back(session);
            }

            peering.set_sessions(session_list);
            peering_list.push_back(peering);
        }

        ShowBgpPeeringConfigResp *resp = new ShowBgpPeeringConfigResp;
        resp->set_peerings(peering_list);
        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowBgpPeeringConfigReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);

    // Request pipeline has single stage to collect peering config info
    // and respond to the request
    RequestPipeline::StageSpec s1;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    s1.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s1.cbFn_ = ShowBgpPeeringConfigHandler::CallbackS1;
    s1.instances_.push_back(0);
    ps.stages_ = list_of(s1);
    RequestPipeline rp(ps);
}

class ShowBgpNeighborConfigHandler {
public:
    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps,
            int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowBgpNeighborConfigReq *req =
            static_cast<const ShowBgpNeighborConfigReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        BgpConfigManager *bcm = bsc->bgp_server->config_manager();
        const BgpConfigData &bcd = bcm->config();

        vector<ShowBgpNeighborConfig> nbr_list;
        for (BgpConfigData::BgpInstanceMap::const_iterator loc1 =
             bcd.instances().begin();
             loc1 != bcd.instances().end(); ++loc1) {

            const BgpInstanceConfig *bic = loc1->second;
            if (!bic->neighbors().size())
                continue;

            for (BgpInstanceConfig::NeighborMap::const_iterator loc2 =
                 bic->neighbors().begin();
                 loc2 != bic->neighbors().end(); ++loc2) {
                const autogen::BgpRouterParams &peer = loc2->second->peer_config();

                ShowBgpNeighborConfig nbr;
                nbr.set_instance_name(loc2->second->InstanceName());
                nbr.set_name(loc2->second->name());
                nbr.set_local_identifier(loc2->second->local_identifier());
                nbr.set_local_as(loc2->second->local_as());
                nbr.set_vendor(peer.vendor);
                nbr.set_autonomous_system(peer.autonomous_system);
                nbr.set_identifier(peer.identifier);
                nbr.set_address(peer.address);
                nbr.set_address_families(loc2->second->address_families());

                nbr_list.push_back(nbr);
            }
        }

        ShowBgpNeighborConfigResp *resp = new ShowBgpNeighborConfigResp;
        resp->set_neighbors(nbr_list);
        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowBgpNeighborConfigReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);

    // Request pipeline has single stage to collect neighbor config info
    // and respond to the request
    RequestPipeline::StageSpec s1;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    s1.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s1.cbFn_ = ShowBgpNeighborConfigHandler::CallbackS1;
    s1.instances_.push_back(0);
    ps.stages_ = list_of(s1);
    RequestPipeline rp(ps);
}

class ShowBgpServerHandler {
public:
    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps, int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowBgpServerReq *req =
            static_cast<const ShowBgpServerReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());

        ShowBgpServerResp *resp = new ShowBgpServerResp;
        SocketIOStats peer_socket_stats;
        bsc->bgp_server->session_manager()->GetRxSocketStats(peer_socket_stats);
        resp->set_rx_socket_stats(peer_socket_stats);

        bsc->bgp_server->session_manager()->GetTxSocketStats(peer_socket_stats);
        resp->set_tx_socket_stats(peer_socket_stats);

        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowBgpServerReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);

    // Request pipeline has single stage to collect neighbor config info
    // and respond to the request
    RequestPipeline::StageSpec s1;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    s1.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s1.cbFn_ = ShowBgpServerHandler::CallbackS1;
    s1.instances_.push_back(0);
    ps.stages_ = list_of(s1);
    RequestPipeline rp(ps);
}

class ShowXmppServerHandler {
public:
    static bool CallbackS1(const Sandesh *sr,
            const RequestPipeline::PipeSpec ps, int stage, int instNum,
            RequestPipeline::InstData *data) {
        const ShowXmppServerReq *req =
            static_cast<const ShowXmppServerReq *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());

        ShowXmppServerResp *resp = new ShowXmppServerResp;
        SocketIOStats peer_socket_stats;
        bsc->xmpp_peer_manager->xmpp_server()->GetRxSocketStats(
                         peer_socket_stats);
        resp->set_rx_socket_stats(peer_socket_stats);

        bsc->xmpp_peer_manager->xmpp_server()->GetTxSocketStats(
                         peer_socket_stats);
        resp->set_tx_socket_stats(peer_socket_stats);

        resp->set_context(req->context());
        resp->Response();
        return true;
    }
};

void ShowXmppServerReq::HandleRequest() const {
    RequestPipeline::PipeSpec ps(this);

    // Request pipeline has single stage to collect neighbor config info
    // and respond to the request
    RequestPipeline::StageSpec s1;
    TaskScheduler *scheduler = TaskScheduler::GetInstance();
    s1.taskId_ = scheduler->GetTaskId("bgp::ShowCommand");
    s1.cbFn_ = ShowXmppServerHandler::CallbackS1;
    s1.instances_.push_back(0);
    ps.stages_ = list_of(s1);
    RequestPipeline rp(ps);
}

class ClearComputeNodeHandler {
public:
    static bool CallbackS1(const Sandesh *sr,
        const RequestPipeline::PipeSpec ps, int stage, int instNum,
        RequestPipeline::InstData *data) {

        const ClearComputeNodeConnection *req =
            static_cast<const ClearComputeNodeConnection *>(ps.snhRequest_.get());
        BgpSandeshContext *bsc =
            static_cast<BgpSandeshContext *>(req->client_context());
        XmppServer *server = bsc->xmpp_peer_manager->xmpp_server();

        ClearComputeNodeConnectionResp *resp = new ClearComputeNodeConnectionResp;
        if (req->get_hostname_or_all() != "all") {
            if (server->ClearConnection(req->get_hostname_or_all())) {
                resp->set_sucess(true);
            } else {
                resp->set_sucess(false);
            }
        } else {
            if (server->ConnectionCount()) {
                server->ClearAllConnections();
                resp->set_sucess(true);
            } else {
                resp->set_sucess(false);
            }
        }

        resp->set_context(req->context());
        resp->Response();
        return(true);
    }
};

void ClearComputeNodeConnection::HandleRequest() const {

    if (ControlNode::GetTestMode() == false) {
        ClearComputeNodeConnectionResp *resp = new ClearComputeNodeConnectionResp;
        resp->set_context(context());
        resp->set_more(false);
        resp->Response();
        return;
    }

    // config task is used to create and delete connection objects.
    // hence use the same task to find the connection
    RequestPipeline::StageSpec s1;
    s1.taskId_ = TaskScheduler::GetInstance()->GetTaskId("bgp::Config");
    s1.instances_.push_back(0);
    s1.cbFn_ = ClearComputeNodeHandler::CallbackS1;

    RequestPipeline::PipeSpec ps(this);
    ps.stages_= list_of(s1);
    RequestPipeline rp(ps);
}

ext: cc

max_stars_repo_name: Juniper/contrail-dev-controller

max_issues_repo_name: Juniper/contrail-dev-controller

max_forks_repo_name: Juniper/contrail-dev-controller


text:
#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/process.hpp>
#include <sstream>
#include <sys/mount.h>
#include <unistd.h>

#include "json.h"

#include "capp.h"
#include "context.h"
#include "oci-hooks.h"
#include "project.h"
#include "utils.h"

#ifndef DOCKER_ARCH
#error Missing DOCKER_ARCH
#endif

static bool is_mounted(const std::string &path) {
  auto file = open_read("/proc/mounts");
  std::string line;
  while (getline(file, line)) {
    auto start = line.find(' ') + 1;
    auto end = line.find(' ', start + 1);
    auto mnt = line.substr(start, end - start);
    if (mnt == path) {
      return true;
    }
  }
  return false;
}

static boost::filesystem::path
overlay_mount(const Context &ctx, const boost::filesystem::path &imgdir,
              const boost::filesystem::path &base) {
  auto rootfs = base / "rootfs";
  boost::filesystem::create_directories(rootfs);
  auto upper = base / ".upper";
  boost::filesystem::create_directories(upper);
  auto work = base / ".work";
  boost::filesystem::create_directories(work);

  if (is_mounted(rootfs.string())) {
    ctx.out() << "Overlay is mounted, skipping re-mount\n";
    return rootfs;
  }

  std::string cmd = "mount -t overlay overlay -o lowerdir=";
  cmd += imgdir.string() + ",upperdir=" + upper.string() +
         ",workdir=" + work.string() + " " + rootfs.string();

  ctx.out() << "Mounting overlay\n";
  if (boost::process::system(cmd) != 0) {
    throw std::runtime_error("Unable to mount overlayfs");
  }
  return rootfs;
}

static boost::filesystem::path get_spec(const std::string &svc_name) {
  auto spec =
      boost::filesystem::current_path() / ".specs" / svc_name / DOCKER_ARCH;
  if (!boost::filesystem::is_regular_file(spec)) {
    spec = boost::filesystem::current_path() / ".specs" / svc_name / "default";
    if (!boost::filesystem::is_regular_file(spec)) {
      throw std::runtime_error("Could not find oci spec file for service");
    }
  }
  return spec;
}

static boost::filesystem::path create_resolv_conf(const Context &ctx,
                                                  const Service &svc) {
  auto host_dns = ctx.host_dns();
  auto resolv_conf = ctx.var_run / svc.name / "resolv.conf";
  boost::filesystem::create_directories(resolv_conf.parent_path());
  auto of = open_write(resolv_conf);

  auto search = svc.dns_search;
  if (search.size() == 0) {
    search = host_dns.search;
  }
  of << "search ";
  for (const auto &s : search) {
    of << s << " ";
  }
  of << "\n";

  auto servers = svc.dns_servers;
  if (servers.size() == 0) {
    servers = host_dns.nameservers;
  }
  for (const auto &s : servers) {
    of << "nameserver " << s << "\n";
  }

  if (svc.dns_opts.size() > 0) {
    of << "options ";
    for (const auto &s : svc.dns_opts) {
      of << s << " ";
    }
    of << "\n";
  }
  return resolv_conf;
}

static void up(const Context &ctx, const Service &svc,
               const std::vector<Volume> &volumes) {
  ctx.out() << "Starting " << svc.name << "\n";
  auto spec = get_spec(svc.name);
  auto hosts = ctx.var_run / "etc_hosts";
  {
    std::ofstream outfile(hosts.string(), std::ios_base::app);
    outfile.close();
  }

  auto resolv_conf = create_resolv_conf(ctx, svc);

  auto imgdir = ctx.var_lib / "images" / svc.name;
  if (!boost::filesystem::is_directory(imgdir)) {
    throw std::runtime_error("Could not find image for service");
  }

  auto rootfs = overlay_mount(ctx, imgdir, ctx.var_lib / "mounts" / svc.name);

  auto dst = ctx.var_run / svc.name / "config.json";
  boost::filesystem::create_directories(dst.parent_path());
  auto sha1 = sha1sum(spec);
  ocispec_create(ctx.app, ctx.volumes(), svc, volumes, spec, dst, rootfs, hosts,
                 resolv_conf);

  ctx.out() << "Execing: crun run -f " << dst << " " << ctx.app << "-"
            << svc.name << "\n";
  const char *crun =
      strdup(boost::process::search_path("crun").string().c_str());
  const char *name = strdup((ctx.app + "-" + svc.name).c_str());
  const char *config = strdup(dst.string().c_str());

  // Why fork/exec just to dump out the content as-is?
  // SystemD's journal uses a socket for the stdout/stderr file descriptor.
  // Docker uses a pipe and many containers, such as nginx, require their
  // /dev/std[out|err] to be a pipe and can fail in very hard to diagnose
  // ways since they'll not actually provide any output when they crash.
  std::string failure = "Unable to execute crun";
  // TODO if tty, this should be changed
  pid_t pid;
  int pipefd[2];
  if (pipe(pipefd) == -1) {
    goto cleanup;
  }
  pid = fork();
  if (pid == -1) {
    goto cleanup;
  } else if (pid == 0) {
    setenv("OCISPEC_SHA1", sha1.c_str(), 1);
    close(pipefd[0]);
    dup2(pipefd[1], STDERR_FILENO);
    dup2(pipefd[1], STDOUT_FILENO);
    if (execl(crun, crun, "run", "-f", config, name, NULL) == -1) {
      throw std::system_error(errno, std::generic_category(),
                              "Unable to execute crun");
    }
  }

  char buffer[BUFSIZ];
  ssize_t bytes_read;
  close(pipefd[1]);
  while (1) {
    bytes_read = read(pipefd[0], buffer, sizeof(buffer));
    if (bytes_read < 0 && errno == EINTR) {
      continue;
    } else if (bytes_read < 0) {
      failure = "Unable to read container output";
      goto cleanup;
    } else if (bytes_read == 0) {
      int status;
      if (waitpid(pid, &status, 0) == -1) {
        failure = "Unable to get exit code from crun";
        goto cleanup;
      }
      if (WIFEXITED(status)) {
        exit(WEXITSTATUS(status));
      }
      throw std::runtime_error("Unknown waitpid rc: " + std::to_string(status));
    }
    fwrite(buffer, bytes_read, 1, stderr);
  }

cleanup:
  umount(rootfs.c_str());
  throw std::system_error(errno, std::generic_category(), failure);
}

void capp_up(const std::string &app_name, const std::string &svc) {
  auto ctx = Context::Load(app_name);
  auto proj = ProjectDefinition::Load("docker-compose.json");

  for (const auto &v : proj.volumes) {
    auto p = ctx.volumes() / v.name;
    if (!boost::filesystem::exists(p)) {
      ctx.out() << "Creating volume: " << v.name << "\n";
      boost::filesystem::create_directories(p);
    }
  }

  up(ctx, proj.get_service(svc), proj.volumes);
}

static void pull(const Context &ctx, const Service &svc) {
  ctx.out() << "Pulling " << svc.name << ": " << svc.image << "\n";
  std::string cmd = "docker pull ";
  boost::process::system(cmd + svc.image);
  ctx.out() << "Extracting\n";
  cmd = "docker create " + svc.image;
  boost::process::ipstream out;
  boost::process::system(cmd, boost::process::std_out > out);
  std::string id;
  out >> id;

  auto imgdir = ctx.var_lib / "images" / svc.name;
  boost::filesystem::create_directories(imgdir);

  boost::process::pipe intermediate;
  boost::process::child docker_export(boost::process::search_path("docker"),
                                      "export", id,
                                      boost::process::std_out > intermediate);
  boost::process::child extract(boost::process::search_path("tar"), "-C",
                                imgdir, "-xf", "-",
                                boost::process::std_in < intermediate);

  docker_export.wait();
  extract.wait();
}

void capp_pull(const std::string &app_name, const std::string &svc) {
  auto ctx = Context::Load(app_name);
  auto proj = ProjectDefinition::Load("docker-compose.json");

  if (svc.size() != 0) {
    pull(ctx, proj.get_service(svc));
  } else {
    for (const auto &svc : proj.services) {
      pull(ctx, svc);
    }
  }
}

static std::vector<std::string> _unit_deps(const std::string &unit) {
  boost::process::ipstream out;
  int rc = boost::process::system("systemctl list-dependencies --plain " + unit,
                                  boost::process::std_out > out);
  if (rc != 0) {
    throw std::runtime_error("Unable to find current units");
  }

  std::vector<std::string> units;
  std::string line;
  bool first_line = true;
  while (std::getline(out, line)) {
    if (!first_line) {
      boost::trim(line);
      if (line.find(unit) == 0) {
        units.push_back(line);
      }
    }
    first_line = false;
  }
  return units;
}

static void _remove_svc(const boost::filesystem::path &units_dir,
                        const std::string &unit) {
  boost::process::system("systemctl stop " + unit);
  boost::process::system("systemctl disable " + unit);
  if (unlink((units_dir / unit).c_str()) != 0) {
    throw std::system_error(errno, std::generic_category(),
                            "Unable to delete " + unit);
  }
}

static std::string _read_file(const boost::filesystem::path &f) {
  // this ignores a file not exists, this is okay based on the
  // copy_if_changed_logic
  std::ifstream t(f.string());
  std::stringstream buffer;
  buffer << t.rdbuf();
  return buffer.str();
}

static bool _copy_if_changed(const std::string &app_name,
                             const boost::filesystem::path &src,
                             const boost::filesystem::path &dst) {
  auto src_content = _read_file(src);
  auto dst_content = _read_file(dst);

  auto exe = boost::filesystem::read_symlink("/proc/self/exe");
  boost::replace_all(src_content, "{{app}}", app_name);
  boost::replace_all(src_content, "{{binary}}", exe.string());
  boost::replace_all(src_content, "{{appdir}}",
                     boost::filesystem::current_path().string());

  if (src_content != dst_content) {
    open_write(dst) << src_content;
    return true;
  }
  return false;
}

void capp_sync_systemd(const boost::filesystem::path &units_dir,
                       const std::string &app_name) {
  auto ctx = Context::Load(app_name);
  auto proj = ProjectDefinition::Load("docker-compose.json");

  bool changed = false;

  auto deps = _unit_deps("capp_" + app_name);
  std::string enables = "capp_" + app_name;

  for (const auto &svc : proj.services) {
    ctx.out() << "Syncing unit " << svc.name << "\n";
    std::string svc_name = "{{app}}_" + svc.name + ".service";
    auto unit = boost::filesystem::current_path() / ".systemd" / svc_name;
    if (!boost::filesystem::exists(unit)) {
      throw std::runtime_error(unit.string() + " does not exist");
    }
    std::string dst = "capp_" + app_name + "_" + svc.name + ".service";
    if (_copy_if_changed(app_name, unit, units_dir / dst)) {
      ctx.out() << " unit changed\n";
      changed = true;
    }
    deps.erase(std::remove(deps.begin(), deps.end(), dst), deps.end());
    enables += " " + dst;
  }
  ctx.out() << "Syncing unit\n";
  auto unit = boost::filesystem::current_path() / ".systemd/{{app}}.service";
  auto dst = units_dir / ("capp_" + app_name + ".service");
  if (_copy_if_changed(app_name, unit, dst)) {
    ctx.out() << " unit changed\n";
    changed = true;
  }

  for (const auto &unit : deps) {
    ctx.out() << "Service has been removed: " << unit << "\n";
    _remove_svc(units_dir, unit);
    changed = true;
  }

  if (changed) {
    ctx.out() << "Reloading systemd\n";
    auto rc = boost::process::system("systemctl daemon-reload");
    if (rc != 0) {
      throw std::runtime_error("Unable to re-configure systemd");
    }

    ctx.out() << "Enabling " << enables << "\n";
    rc = boost::process::system("systemctl enable " + enables);
    if (rc != 0) {
      throw std::runtime_error("Unable to re-configure systemd");
    }
  }
}

static void status(const Context &ctx, const Service &svc) {
  ctx.out() << "Checking status of " << svc.name << "\n";
  boost::filesystem::path p("/var/run/crun");
  p = p / (ctx.app + "-" + svc.name);

  nlohmann::json data;
  try {
    open_read(p / "status") >> data;
  } catch (const std::exception &ex) {
    ctx.out() << " not running\n";
    return;
  }

  auto pid = data["pid"].get<int>();
  ctx.out() << " pid(" << pid << ")";
  boost::filesystem::path proc("/proc");
  auto f = open_read(proc / std::to_string(pid) / "stat");
  std::string buf;
  // parent is 4th item
  f >> buf;
  f >> buf;
  f >> buf;
  f >> buf;
  f = open_read(proc / buf / "environ");
  f >> buf;
  auto idx = buf.find("OCISPEC_SHA1");
  idx += 13;
  buf = buf.substr(idx, 40);
  auto sha1 = sha1sum(get_spec(svc.name));
  if (sha1 == buf) {
    ctx.out() << " up-to-date\n";
  } else {
    ctx.out() << " needs-updating\n";
  }
}

void capp_status(const std::string &app_name) {
  auto ctx = Context::Load(app_name);
  auto proj = ProjectDefinition::Load("docker-compose.json");
  for (const auto &svc : proj.services) {
    status(ctx, svc);
  }
}

max_stars_repo_name: doanac/capp-run

max_issues_repo_name: doanac/capp-run

max_forks_repo_name: doanac/capp-run


text:
//
//  Copyright (c) 2019 Alexander Grund
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/nowide/config.hpp>

#include <boost/nowide/fstream.hpp>

#include <boost/nowide/cstdio.hpp>
#include <iterator>
#include <utility>

#include "test.hpp"

namespace nw = boost::nowide;

void create_file(const std::string& filename, const std::string& contents)
{
    nw::ofstream f(filename, std::ios::trunc);
    TEST(f << contents);
}

template<typename T>
std::string get_file_contents(T& stream)
{
    TEST(stream);
    std::string s((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
    return s;
}

std::string get_file_contents(const std::string& filename)
{
    nw::ifstream f(filename);
    return get_file_contents(f);
}

nw::ifstream make_ifstream(const std::string& filename)
{
    nw::ifstream f(filename);
    TEST(f);
    std::string s;
    TEST(f >> s);
    TEST(s == "Hello");
    return f;
}

void test_ifstream(const std::string& filename)
{
    create_file(filename, "Hello\nWorld");
    // Move construct
    {
        nw::ifstream f = make_ifstream(filename);
        TEST(f);
        std::string s;
        TEST(f >> s);
        TEST(s == "World");
    }
    // Move assign
    {
        nw::ifstream f;
        {
            nw::ifstream f2 = make_ifstream(filename);
            f = std::move(f2);
        }
        TEST(f);
        std::string s;
        TEST(f >> s);
        TEST(s == "World");
    }
    // Swap
    {
        nw::ifstream f;
        {
            nw::ifstream f2 = make_ifstream(filename);
            swap(f, f2);
            TEST(!f2.is_open());
        }
        TEST(f);
        std::string s;
        TEST(f >> s);
        TEST(s == "World");
    }
    TEST(nw::remove(filename.c_str()) == 0);
}

nw::ofstream make_ofstream(const std::string& filename)
{
    nw::ofstream f(filename);
    TEST(f);
    TEST(f << "Hello");
    return f;
}

void test_ofstream(const std::string& filename)
{
    // Move construct
    {
        nw::ofstream f = make_ofstream(filename);
        TEST(f);
        TEST(f << " world");
        f.close();
        TEST(get_file_contents(filename) == "Hello world");
    }
    // Move assign
    {
        nw::ofstream f;
        {
            nw::ofstream f2 = make_ofstream(filename);
            f = std::move(f2);
        }
        TEST(f);
        TEST(f << " world");
        f.close();
        TEST(get_file_contents(filename) == "Hello world");
    }
    // Swap
    {
        nw::ofstream f;
        {
            nw::ofstream f2 = make_ofstream(filename);
            swap(f, f2);
            TEST(!f2.is_open());
        }
        TEST(f);
        TEST(f << " world");
        f.close();
        TEST(get_file_contents(filename) == "Hello world");
    }
    TEST(nw::remove(filename.c_str()) == 0);
}

nw::fstream make_fstream(const std::string& filename)
{
    create_file(filename, "");
    nw::fstream f(filename);
    TEST(f << "Hello");
    return f;
}

void test_fstream(const std::string& filename)
{
    // Move construct
    {
        nw::fstream f = make_fstream(filename);
        TEST(f);
        TEST(f << " world");
        f.seekg(0);
        TEST(get_file_contents(f) == "Hello world");
    }
    // Move assign
    {
        nw::fstream f;
        {
            nw::fstream f2 = make_fstream(filename);
            f = std::move(f2);
        }
        TEST(f);
        TEST(f << " world");
        f.seekg(0);
        TEST(get_file_contents(f) == "Hello world");
    }
    // Swap
    {
        nw::fstream f;
        {
            nw::fstream f2 = make_fstream(filename);
            swap(f, f2);
            TEST(!f2.is_open());
        }
        TEST(f);
        TEST(f << " world");
        f.seekg(0);
        TEST(get_file_contents(f) == "Hello world");
    }
    TEST(nw::remove(filename.c_str()) == 0);
}

void test_main(int, char** argv, char**)
{
    const std::string exampleFilename = std::string(argv[0]) + "-\xd7\xa9-\xd0\xbc-\xce\xbd.txt";
    test_ifstream(exampleFilename);
    test_ofstream(exampleFilename);
    test_fstream(exampleFilename);
}

max_stars_repo_name: DouglasHeriotDesign/nowide

max_issues_repo_name: DouglasHeriotDesign/nowide


text:
#include "actions.hpp"

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <map>
#include <vector>

#include <prc/parser/as_type.hpp>
#include <prc/range.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/fusion/adapted/std_pair.hpp>

namespace fs = std::filesystem;
namespace x3 = boost::spirit::x3;

namespace prc::actions
{
namespace
{
x3::rule<struct range_name_class,
         std::vector<std::pair<std::string, std::string>>> const _range_name =
    "range name";

auto const _position = parser::as<
    std::string>[x3::lexeme[+(x3::upper | x3::digit | x3::char_('+'))]];

auto const _action = x3::lexeme[+(x3::char_ - '_')];
auto const _range_name_def =
    parser::as<std::pair<std::string, std::string>>[_position >> '_' >>
                                                    _action] %
    '_';

BOOST_SPIRIT_DEFINE(_range_name);

struct flattened_range
{
  fs::path fullpath;
  prc::folder* parent_folder;
  prc::range* range;
};

struct parent_child_range
{
  range* parent;
  range* child;
  fs::path parent_path;
  fs::path child_path;
  prc::folder* child_parent_folder;
  std::string subrange_name;
};

struct parent_range_info
{
  std::string name;
  std::string subrange_name;
};

bool has_vsxbet_folders(folder& root)
{
  auto it =
      std::find_if(root.entries().begin(), root.entries().end(), [](auto& f) {
        if (auto p = boost::variant2::get_if<folder>(&f))
          return boost::algorithm::contains(p->name(), "vs_");
        return false;
      });
  return it != root.entries().end();
}

void recurse_fill_ranges(folder& root,
                         fs::path const& current_path,
                         std::vector<flattened_range>& out)
{
  for (auto& entry : root.entries())
  {
    if (auto p = boost::variant2::get_if<range>(&entry))
      out.push_back({current_path / p->name(), &root, p});
    else
    {
      auto& subfolder = boost::variant2::get<folder>(entry);
      recurse_fill_ranges(subfolder, current_path / subfolder.name(), out);
    }
  }
}

int recurse_count_subranges(range const& r)
{
  auto count = 1;
  for (auto const& sub : r.subranges())
    count += recurse_count_subranges(sub);
  return count;
}

std::vector<std::pair<std::string, std::string>> parse_range_name(
    std::string const& input)
{
  std::vector<std::pair<std::string, std::string>> ret;
  auto b = input.begin();
  auto r =
      x3::phrase_parse(b, input.end(), _range_name >> x3::eoi, x3::space, ret);
  if (!r || b != input.end())
  {
    std::cout << "Could not parse range name: " << input << ", skipping"
              << std::endl;
    return {};
  }
  return ret;
}

void replace_parent_range(parent_child_range& p)
{
  auto const& subranges = p.parent->subranges();
  auto const subrange_it =
      std::find_if(subranges.begin(), subranges.end(), [&](auto& s) {
        return s.name() == p.subrange_name;
      });
  if (subrange_it == subranges.end())
  {
    // TODO add fmt once conan-center-index boost 1.75.0 is fixed
    std::cout << p.parent_path << ": no subrange " << p.subrange_name
              << std::endl;

    std::cout << p.child_path << ": could not replace parent range"
              << std::endl;
    return;
  }
  auto const& parent_range_elems = subrange_it->elems();
  auto const adjusted_parent_elems =
      adjust_weights(parent_range_elems, *p.parent);
  auto adjusted_elems = adjust_weights(adjusted_parent_elems, *p.child);
  p.child->set_elems(std::move(adjusted_elems));
  std::cout << p.child_path << ": replaced parent range by subrange "
            << p.subrange_name << " of " << p.parent_path << std::endl;
}

void nest_range(parent_child_range& p)
{
  auto subrange = p.parent->find_subrange(p.subrange_name);
  if (!subrange)
  {
    // TODO add fmt once conan-center-index boost 1.75.0 is fixed
    std::cout << p.parent_path << ": no subrange " << p.subrange_name
              << std::endl;

    std::cout << p.child_path << ": could not nest" << std::endl;
    return;
  }

  auto const prefix = boost::algorithm::erase_all_copy(
      p.parent_path.filename().string(), "_strategy");
  auto const new_name = boost::algorithm::erase_all_copy(
      p.child_path.filename().string(), prefix + '_' + p.subrange_name + '_');
  // nesting is a bit weird in equilab, ranges have 100% of their parent
  std::vector<prc::combo> combos;
  for (auto const& [w, e] : p.child->elems())
  {
    auto c = prc::expand_combos(e);
    combos.insert(combos.end(), c.begin(), c.end());
  }
  auto new_range = *p.child;
  new_range.set_name(new_name);
  new_range.set_elems({{100.0, prc::reduce_combos(combos)}});
  // mimick equilab selection color
  new_range.set_rgb(0xff6ebaff);
  subrange->add_subrange(std::move(new_range));
  std::cout << p.child_path << ": nested into subrange " << p.subrange_name
            << " of " << p.parent_path << " as " << new_name << std::endl;
}

std::optional<parent_range_info> get_parent_range_info(std::string const& name)
{
  auto const parts = parse_range_name(name);
  if (parts.empty())
    return std::nullopt;
  auto const& position = parts.back().first;
  auto const pos_it =
      std::find_if(parts.rbegin() + 1, parts.rend(), [&position](auto& p) {
        return p.first == position;
      });
  if (pos_it == parts.rend())
    return std::nullopt;
  std::string parent_name;
  for (auto it = parts.begin(); it != pos_it.base() - 1; ++it)
  {
    auto const& [pos, action] = *it;
    parent_name += pos + '_' + action + '_';
  }
  parent_name += position + "_strategy";
  return {{std::move(parent_name), pos_it->second}};
}

std::vector<flattened_range> flatten_ranges(folder& f,
                                            fs::path const& current_path)
{
  std::vector<flattened_range> flattened_ranges;
  recurse_fill_ranges(f, current_path, flattened_ranges);

  std::sort(
      flattened_ranges.begin(),
      flattened_ranges.end(),
      [](auto& lhs, auto& rhs) {
        if (lhs.fullpath.parent_path() == rhs.fullpath.parent_path())
        {
          if (lhs.fullpath.string().size() == rhs.fullpath.string().size())
            return lhs.fullpath.string() < rhs.fullpath.string();
          return lhs.fullpath.string().size() < rhs.fullpath.string().size();
        }
        return lhs.fullpath.parent_path().string() <
               rhs.fullpath.parent_path().string();
      });
  return flattened_ranges;
}

std::vector<parent_child_range> find_parent_ranges(
    std::vector<flattened_range> const& flattened_ranges)
{
  std::vector<parent_child_range> parent_ranges;

  for (auto it = flattened_ranges.rbegin(); it != flattened_ranges.rend(); ++it)
  {
    auto info = get_parent_range_info(it->range->name());
    if (info)
    {
      auto const parent_it =
          std::find_if(it + 1, flattened_ranges.rend(), [&info](auto& r) {
            return r.range->name() == info->name;
          });
      if (parent_it != flattened_ranges.rend())
      {
        parent_ranges.push_back({parent_it->range,
                                 it->range,
                                 parent_it->fullpath,
                                 it->fullpath,
                                 it->parent_folder,
                                 std::move(info->subrange_name)});
      }
    }
  }
  std::reverse(parent_ranges.begin(), parent_ranges.end());
  return parent_ranges;
}

template <typename T>
auto get_entry_name(T& entry)
{
  return boost::variant2::visit([&](auto& e) { return e.name(); }, entry);
}

template <typename T>
void set_entry_name(T& entry, std::string const& new_name)
{
  boost::variant2::visit([&](auto& e) { e.set_name(new_name); }, entry);
}

auto entry_contains(std::string const& str)
{
  return [str](auto& e) {
    return boost::algorithm::contains(get_entry_name(e), str);
  };
}

enum range_type
{
  all_in,
  raise,
  call,
  fold,
};

std::map<int, range_type> const rgb_to_range_types{
    {0xFF8B0000, range_type::all_in},
    {0xffe9967a, range_type::raise},
    {0xFF8FBC8B, range_type::call},
    {0xff6da2c0, range_type::fold}};

bool contains_percent(range const& r)
{
  if (boost::algorithm::contains(r.name(), "%"))
    return true;
  return std::any_of(
      r.subranges().begin(), r.subranges().end(), [](auto const& s) {
        return boost::algorithm::contains(s.name(), "%");
      });
}
}

inline namespace folder_actions
{
namespace
{
auto const has_only_fold = [](auto& r) {
  return r.subranges().size() == 1 && r.subranges().front().name() == "Fold";
};

auto const has_no_subranges = [](auto& r) { return r.subranges().empty(); };

auto const has_too_many_players = [](auto& r) {
  auto const parts = parse_range_name(r.name());
  if (parts.size() < 3)
    return false;
  // only keep squeeze spots when 3 players
  std::vector<std::string> positions;
  std::vector<std::string> actions;
  auto const& current_pos = parts.back().first;

  for (auto const& [pos, action] : parts)
  {
    positions.push_back(pos);
    actions.push_back(action);
  }
  {
    auto sorted_pos = positions;
    std::sort(sorted_pos.begin(), sorted_pos.end());
    sorted_pos.erase(std::unique(sorted_pos.begin(), sorted_pos.end()),
                     sorted_pos.end());
    if (sorted_pos.size() > 3)
      return true;
  }
  if (positions[0] != current_pos && positions[1] != current_pos)
  {
    // keep squeeze spots and SRP with multiple calls
    if (!std::all_of(actions.begin() + 1, actions.end() - 1, [](auto& a) {
          return a == "Call";
        }))
    {
      return true;
    }
  }
  // don't keep when we open + raise + other raise
  if (positions[0] == current_pos)
  {
    if (positions[2] != current_pos)
    {
      if (actions[1] != "Call" && actions[2] != "Call")
        return true;
    }
  }
  return false;
};
}

folder_action remove_useless_ranges()
{
  return [](folder& f, fs::path const& current_path) {
    auto& entries = f.entries();
    entries.erase(
        std::remove_if(entries.begin(),
                       entries.end(),
                       [&](auto& e) {
                         if (auto p = boost::variant2::get_if<range>(&e))
                         {
                           auto b = has_only_fold(*p) || has_no_subranges(*p) ||
                                    has_too_many_players(*p);
                           if (b)
                           {
                             std::cout << "Removing "
                                       << current_path / p->name() << std::endl;
                           }
                           return b;
                         }
                         return false;
                       }),
        entries.end());
    return true;
  };
}

folder_action fix_parent_ranges()
{
  return [](auto& folder, fs::path const& current_path) {
    // SB is the only position where limp raise happens
    if (!has_vsxbet_folders(folder) && folder.name() != "SB")
      return true;
    auto const flattened_ranges = flatten_ranges(folder, current_path);
    auto parent_ranges = find_parent_ranges(flattened_ranges);

    for (auto& elem : parent_ranges)
      replace_parent_range(elem);
    return false;
  };
}

folder_action nest_parent_ranges()
{
  return [](auto& folder, fs::path const& current_path) {
    if (!has_vsxbet_folders(folder))
      return true;
    auto const flattened_ranges = flatten_ranges(folder, current_path);
    auto parent_ranges = find_parent_ranges(flattened_ranges);

    for (auto it = parent_ranges.rbegin(); it != parent_ranges.rend(); ++it)
      nest_range(*it);
    // we can remove now, iterators can be invalidated
    for (auto it = parent_ranges.rbegin(); it != parent_ranges.rend(); ++it)
    {
      it->child_parent_folder->remove_entry(it->child_path.filename().string());
      std::cout << "removed " << it->child_path << std::endl;
    }
    return true;
  };
}

folder_action remove_empty_folders()
{
  return [](auto& f, fs::path const& current_path) {
    auto& entries = f.entries();
    entries.erase(
        std::remove_if(entries.begin(),
                       entries.end(),
                       [&](auto& e) {
                         if (auto p = boost::variant2::get_if<folder>(&e))
                         {
                           if (p->entries().empty())
                           {
                             std::cout << "removing empty folder: "
                                       << current_path / p->name() << std::endl;
                             return true;
                           }
                         }
                         return false;
                       }),
        entries.end());
    return true;
  };
}
}

inline namespace range_actions
{
range_action replace_in_range_name(std::string const& old_str,
                                   std::string const& new_str)
{
  return [old_str, new_str](range& r, fs::path const& abs_parent_path) {
    if (boost::algorithm::contains(r.name(), old_str))
    {
      std::cout << abs_parent_path / r.name() << ": rename to ";
      r.set_name(
          boost::algorithm::replace_all_copy(r.name(), old_str, new_str));
      std::cout << r.name() << std::endl;
    }
  };
}

range_action change_color(std::string const& range_name, int rgb)
{
  return [range_name, rgb](range& r, fs::path const& abs_parent_path) {
    if (r.name() == range_name && r.rgb() != rgb)
    {
      std::cout << abs_parent_path / r.name() << ": changing color from "
                << std::hex << r.rgb() << " to " << rgb << std::dec
                << std::endl;
      r.set_rgb(rgb);
    }
  };
}

range_action change_color_ends_with(std::string const& str, int rgb)
{
  return [str, rgb](range& r, fs::path const& abs_parent_path) {
    if (boost::algorithm::ends_with(r.name(), str) && r.rgb() != rgb)
    {
      std::cout << abs_parent_path / r.name() << ": changing color from "
                << std::hex << r.rgb() << " to " << rgb << std::dec
                << std::endl;
      r.set_rgb(rgb);
    }
  };
}

range_action set_unassigned_to_subrange(std::string const& range_name, int rgb)
{
  return [range_name, rgb](range& r, fs::path const& abs_parent_path) {
    if (r.subranges().empty())
      return;
    for (auto& sub : r.subranges())
    {
      if (sub.name() == range_name)
        return;
    }

    auto unassigned = prc::unassigned_elems(r);
    if (!unassigned.empty())
    {
      r.add_subrange({range_name, std::move(unassigned), rgb});
      std::cout << abs_parent_path / r.name() << ": set unassigned range to "
                << range_name << std::endl;
    }
  };
}

range_action move_subrange_at_end(std::string const& range_name)
{
  return [range_name](range& r, fs::path const& abs_parent_path) {
    auto const end = r.subranges().end();
    auto it = std::find_if(r.subranges().begin(), end, [&](auto& s) {
      return s.name() == range_name;
    });
    if (it != end)
      std::rotate(it, it + 1, end);
  };
}

range_action sort_subranges()
{
  return [](range& r, fs::path const& abs_parent_path) {
    std::sort(r.subranges().begin(),
              r.subranges().end(),
              [](auto const& lhs, auto const& rhs) {
                return rgb_to_range_types.at(lhs.rgb()) <
                       rgb_to_range_types.at(rhs.rgb());
              });
  };
}

range_action count_max_ranges()
{
  return [](range& r, fs::path const& abs_parent_path) {
    std::cout << recurse_count_subranges(r) << " groups in "
              << abs_parent_path / r.name() << std::endl;
  };
}

// TODO refactor this mess
range_action percents_to_bb()
{
  return [](range& r, fs::path const& abs_parent_path) {
    if (!contains_percent(r))
      return;
    auto parts = parse_range_name(r.name());
    if (parts.empty())
      return;

    auto current_pot = 2.5;
    auto last_bet = 1.0;
    std::map<std::string, double> positions_last_bet{{"UTG", 0},
                                                     {"UTG+1", 0},
                                                     {"LJ", 0},
                                                     {"HJ", 0},
                                                     {"CO", 0},
                                                     {"BTN", 0},
                                                     {"SB", 0.5},
                                                     {"BB", 1}};

    // TODO refactor ends_with%
    // keep track of last bet for every position, the count is messed up!!
    // e.g. SB 0.5, BB 1.0
    // if raise call then reraise and call, the count is broken!
    for (auto& [pos, action] : parts)
    {
      if (action == "Fold")
        continue;
      else if (boost::algorithm::ends_with(action, "bb"))
      {
        double bbs = 0;
        x3::phrase_parse(action.begin(),
                         action.end(),
                         x3::double_ >> x3::lit("bb"),
                         x3::space,
                         bbs);
        last_bet = bbs;
      }
      else if (boost::algorithm::ends_with(action, "%"))
      {
        double percent = 0;
        x3::phrase_parse(action.begin(),
                         action.end(),
                         x3::double_ >> x3::lit("%"),
                         x3::space,
                         percent);
        auto const amount_to_call = last_bet - positions_last_bet.at(pos);
        auto const new_last_bet =
            last_bet + (current_pot + amount_to_call) * (percent / 100.f);
        // when RFI percent is 28%, it gives 1.98, the actual percent is
        // around 28.6, which is not in the range name
        last_bet = std::max(new_last_bet, 2 * last_bet);
        std::stringstream ss;
        ss << std::fixed << std::setprecision(2) << last_bet;
        action = ss.str();
        if (action.back() == '0')
          action.pop_back();
        action += "bb";
      }
      current_pot += last_bet - positions_last_bet.at(pos);
      positions_last_bet.at(pos) = last_bet;
    }

    std::string new_str;
    for (auto const& [pos, action] : parts)
      new_str += pos + '_' + action + '_';
    new_str.pop_back();
    // don't care about nesting
    for (auto& sub : r.subranges())
    {
      if (boost::algorithm::ends_with(sub.name(), "%"))
      {
        double percent = 0;
        x3::phrase_parse(sub.name().begin(),
                         sub.name().end(),
                         x3::double_ >> x3::lit("%"),
                         x3::space,
                         percent);

        auto const amount_to_call =
            last_bet - positions_last_bet.at(parts.back().first);
        auto const final_bet =
            last_bet + (current_pot + amount_to_call) * (percent / 100.f);
        std::stringstream ss;
        ss << std::fixed << std::setprecision(2) << final_bet;
        auto action = ss.str();
        if (action.back() == '0')
          action.pop_back();
        action += "bb";
        std::cout << abs_parent_path / r.name() / sub.name() << ": rename to "
                  << action << std::endl;
        sub.set_name(action);
      }
    }
    std::cout << abs_parent_path / r.name() << ": rename to " << new_str
              << std::endl;
    r.set_name(new_str);
  };
}
}
}

max_stars_repo_name: theodelrieu/prc

max_issues_repo_name: theodelrieu/prc

max_forks_repo_name: theodelrieu/prc