json::value dointernal (rpc::context& context) { // used for debug or special-purpose rpc commands if (!context.params.ismember ("internal_command")) return rpcerror (rpcinvalid_params); auto name = context.params["internal_command"].asstring (); auto params = context.params["params"]; for (auto* h = rpc::internalhandler::headhandler; h; ) { if (name == h->name_) { writelog (lswarning, rpchandler) << "internal command " << name << ": " << params; json::value ret = h->handler_ (params); writelog (lswarning, rpchandler) << "internal command returns: " << ret; return ret; } h = h->nexthandler_; } return rpcerror (rpcbad_syntax); }
void threadmain(int argc, char **argv) { int i; Path *p; fmtinstall('H', encodefmt); fmtinstall('P', pathfmt); fmtinstall('R', rpcfmt); fmtinstall('$', statfmt); fmtinstall('V', vtimefmt); ARGBEGIN{ case 'D': debug |= dbglevel(EARGF(usage())); break; case 'V': traversion(); default: usage(); }ARGEND if(argc < 1) usage(); startclient(); repl = dialreplica(argv[0]); argc--; argv++; fprint(2, "scan..."); if(argc == 0){ fprint(2, "%P...", nil); if(rpcstat(repl, nil) == nil) sysfatal("rpcstat: %s", rpcerror()); }else{ for(i=0; i<argc; i++){ p = strtopath(argv[i]); fprint(2, "%P...", p); if(rpcstat(repl, p) == nil) sysfatal("rpcstat: %s", rpcerror()); } } fprint(2, "hangup..."); rpchangup(repl); fprint(2, "close..."); replclose(repl); fprint(2, "exit\n"); threadexitsall(nil); }
static void kidthread(void *a) { void *p; Kids *k; k = a; threadsetname("kidthread"); startclient(); k->nk = rpckids(k->repl, k->p, &k->k); if(k->nk < 0) k->err = rpcerror(); endclient(); threadstate("kidthread end"); p = k->c; send(k->c, k); threadstate("DONE %p", p); }
// { // secret: <string> // } json::value dovalidationseed (rpc::context& context) { auto lock = getapp().masterlock(); json::value obj (json::objectvalue); if (!context.params.ismember ("secret")) { std::cerr << "unset validation seed." << std::endl; getconfig ().validation_seed.clear (); getconfig ().validation_pub.clear (); getconfig ().validation_priv.clear (); } else if (!getconfig ().validation_seed.setseedgeneric ( context.params["secret"].asstring ())) { getconfig ().validation_pub.clear (); getconfig ().validation_priv.clear (); return rpcerror (rpcbad_seed); } else { auto& seed = getconfig ().validation_seed; auto& pub = getconfig ().validation_pub; pub = rippleaddress::createnodepublic (seed); getconfig ().validation_priv = rippleaddress::createnodeprivate (seed); obj["validation_public_key"] = pub.humannodepublic (); obj["validation_seed"] = seed.humanseed (); obj["validation_key"] = seed.humanseed1751 (); } return obj; }
// { // account: <account>|<account_public_key> // account_index: <number> // optional, defaults to 0. // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } json::value doaccountlines (rpc::context& context) { auto const& params (context.params); if (! params.ismember (jss::account)) return rpc::missing_field_error ("account"); ledger::pointer ledger; json::value result (rpc::lookupledger (params, ledger, context.netops)); if (! ledger) return result; std::string strident (params[jss::account].asstring ()); bool bindex (params.ismember (jss::account_index)); int iindex (bindex ? params[jss::account_index].asuint () : 0); rippleaddress rippleaddress; json::value const jv (rpc::accountfromstring (ledger, rippleaddress, bindex, strident, iindex, false, context.netops)); if (! jv.empty ()) { for (json::value::const_iterator it (jv.begin ()); it != jv.end (); ++it) result[it.membername ()] = it.key (); return result; } if (! ledger->hasaccount (rippleaddress)) return rpcerror (rpcact_not_found); std::string strpeer (params.ismember (jss::peer) ? params[jss::peer].asstring () : ""); bool bpeerindex (params.ismember (jss::peer_index)); int ipeerindex (bindex ? params[jss::peer_index].asuint () : 0); rippleaddress rippleaddresspeer; if (! strpeer.empty ()) { result[jss::peer] = rippleaddress.humanaccountid (); if (bpeerindex) result[jss::peer_index] = ipeerindex; result = rpc::accountfromstring (ledger, rippleaddresspeer, bpeerindex, strpeer, ipeerindex, false, context.netops); if (! result.empty ()) return result; } account rapeeraccount; if (rippleaddresspeer.isvalid ()) rapeeraccount = rippleaddresspeer.getaccountid (); unsigned int limit; if (params.ismember (jss::limit)) { auto const& jvlimit (params[jss::limit]); if (! jvlimit.isintegral ()) return rpc::expected_field_error ("limit", "unsigned integer"); limit = jvlimit.isuint () ? jvlimit.asuint () : std::max (0, jvlimit.asint ()); if (context.role != role::admin) { limit = std::max (rpc::tuning::minlinesperrequest, std::min (limit, rpc::tuning::maxlinesperrequest)); } } else { limit = rpc::tuning::defaultlinesperrequest; } json::value& jsonlines (result[jss::lines] = json::arrayvalue); account const& raaccount(rippleaddress.getaccountid ()); visitdata visitdata = { {}, raaccount, rippleaddresspeer, rapeeraccount }; unsigned int reserve (limit); uint256 startafter; std::uint64_t starthint; if (params.ismember (jss::marker)) { // we have a start point. use limit - 1 from the result and use the // very last one for the resume. json::value const& marker (params[jss::marker]); if (! marker.isstring ()) return rpc::expected_field_error ("marker", "string"); startafter.sethex (marker.asstring ()); sle::pointer sleline (ledger->getslei (startafter)); if (sleline == nullptr || sleline->gettype () != ltripple_state) return rpcerror (rpcinvalid_params); if (sleline->getfieldamount (sflowlimit).getissuer () == raaccount) starthint = sleline->getfieldu64 (sflownode); else if (sleline->getfieldamount (sfhighlimit).getissuer () == raaccount) starthint = sleline->getfieldu64 (sfhighnode); else return rpcerror (rpcinvalid_params); // caller provided the first line (startafter), add it as first result auto const line (ripplestate::makeitem (raaccount, sleline)); if (line == nullptr) return rpcerror (rpcinvalid_params); addline (jsonlines, *line, ledger); visitdata.items.reserve (reserve); } else { starthint = 0; // we have no start point, limit should be one higher than requested. visitdata.items.reserve (++reserve); } if (! ledger->visitaccountitems (raaccount, startafter, starthint, reserve, [&visitdata](sle::ref slecur) { auto const line (ripplestate::makeitem (visitdata.accountid, slecur)); if (line != nullptr && (! visitdata.rippleaddresspeer.isvalid () || visitdata.rapeeraccount == line->getaccountidpeer ())) { visitdata.items.emplace_back (line); return true; } return false; })) { return rpcerror (rpcinvalid_params); } if (visitdata.items.size () == reserve) { result[jss::limit] = limit; ripplestate::pointer line (visitdata.items.back ()); result[jss::marker] = to_string (line->peeksle ().getindex ()); visitdata.items.pop_back (); } result[jss::account] = rippleaddress.humanaccountid (); for (auto const& item : visitdata.items) addline (jsonlines, *item.get (), ledger); context.loadtype = resource::feemediumburdenrpc; return result; }
// { // account: account, // ledger_index_min: ledger_index, // ledger_index_max: ledger_index, // binary: boolean, // optional, defaults to false // count: boolean, // optional, defaults to false // descending: boolean, // optional, defaults to false // offset: integer, // optional, defaults to 0 // limit: integer // optional // } json::value doaccounttxold (rpc::context& context) { rippleaddress raaccount; std::uint32_t offset = context.params.ismember ("offset") ? context.params["offset"].asuint () : 0; int limit = context.params.ismember ("limit") ? context.params["limit"].asuint () : -1; bool bbinary = context.params.ismember ("binary") && context.params["binary"].asbool (); bool bdescending = context.params.ismember ("descending") && context.params["descending"].asbool (); bool bcount = context.params.ismember ("count") && context.params["count"].asbool (); std::uint32_t uledgermin; std::uint32_t uledgermax; std::uint32_t uvalidatedmin; std::uint32_t uvalidatedmax; bool bvalidated = context.netops.getvalidatedrange ( uvalidatedmin, uvalidatedmax); if (!context.params.ismember ("account")) return rpcerror (rpcinvalid_params); if (!raaccount.setaccountid (context.params["account"].asstring ())) return rpcerror (rpcact_malformed); if (offset > 3000) return rpcerror (rpcatx_deprecated); context.loadtype = resource::feehighburdenrpc; // deprecated if (context.params.ismember ("ledger_min")) { context.params["ledger_index_min"] = context.params["ledger_min"]; bdescending = true; } // deprecated if (context.params.ismember ("ledger_max")) { context.params["ledger_index_max"] = context.params["ledger_max"]; bdescending = true; } if (context.params.ismember ("ledger_index_min") || context.params.ismember ("ledger_index_max")) { std::int64_t iledgermin = context.params.ismember ("ledger_index_min") ? context.params["ledger_index_min"].asint () : -1; std::int64_t iledgermax = context.params.ismember ("ledger_index_max") ? context.params["ledger_index_max"].asint () : -1; if (!bvalidated && (iledgermin == -1 || iledgermax == -1)) { // don't have a validated ledger range. return rpcerror (rpclgr_idxs_invalid); } uledgermin = iledgermin == -1 ? uvalidatedmin : iledgermin; uledgermax = iledgermax == -1 ? uvalidatedmax : iledgermax; if (uledgermax < uledgermin) { return rpcerror (rpclgr_idxs_invalid); } } else { ledger::pointer l; json::value ret = rpc::lookupledger (context.params, l, context.netops); if (!l) return ret; uledgermin = uledgermax = l->getledgerseq (); } int count = 0; #ifndef beast_debug try { #endif json::value ret (json::objectvalue); ret["account"] = raaccount.humanaccountid (); json::value& jvtxns = (ret["transactions"] = json::arrayvalue); if (bbinary) { auto txns = context.netops.getaccounttxsb ( raaccount, uledgermin, uledgermax, bdescending, offset, limit, context.role == role::admin); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { ++count; json::value& jvobj = jvtxns.append (json::objectvalue); std::uint32_t uledgerindex = std::get<2> (*it); jvobj["tx_blob"] = std::get<0> (*it); jvobj["meta"] = std::get<1> (*it); jvobj["ledger_index"] = uledgerindex; jvobj["validated"] = bvalidated && uvalidatedmin <= uledgerindex && uvalidatedmax >= uledgerindex; } } else { auto txns = context.netops.getaccounttxs ( raaccount, uledgermin, uledgermax, bdescending, offset, limit, context.role == role::admin); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { ++count; json::value& jvobj = jvtxns.append (json::objectvalue); if (it->first) jvobj["tx"] = it->first->getjson (1); if (it->second) { std::uint32_t uledgerindex = it->second->getlgrseq (); jvobj["meta"] = it->second->getjson (0); jvobj["validated"] = bvalidated && uvalidatedmin <= uledgerindex && uvalidatedmax >= uledgerindex; } } } //add information about the original query ret["ledger_index_min"] = uledgermin; ret["ledger_index_max"] = uledgermax; ret["validated"] = bvalidated && uvalidatedmin <= uledgermin && uvalidatedmax >= uledgermax; ret["offset"] = offset; // we no longer return the full count but only the count of returned // transactions. computing this count was two expensive and this api is // deprecated anyway. if (bcount) ret["count"] = count; if (context.params.ismember ("limit")) ret["limit"] = limit; return ret; #ifndef beast_debug } catch (...) { return rpcerror (rpcinternal); } #endif }
// { // account: [<account>|<account_public_key>] // peer: [<account>|<account_public_key>] // currency: <currency> // ledger_hash : <ledger> // ledger_index : <ledger_index> // } json::value doaccountasset (rpc::context& context) { auto const& params(context.params); if (!params.ismember(jss::account)) return rpc::missing_field_error("account"); if (!params.ismember(jss::peer)) return rpc::missing_field_error("peer"); if (!params.ismember(jss::currency)) return rpc::missing_field_error("currency"); ledger::pointer ledger; json::value result(rpc::lookupledger(params, ledger, context.netops)); if (!ledger) return result; std::string strident(params[jss::account].asstring()); bool bindex(params.ismember(jss::account_index)); int iindex(bindex ? params[jss::account_index].asuint() : 0); rippleaddress rippleaddress; json::value const jv(rpc::accountfromstring(ledger, rippleaddress, bindex, strident, iindex, false, context.netops)); if (!jv.empty()) { for (json::value::const_iterator it(jv.begin()); it != jv.end(); ++it) result[it.membername()] = it.key(); return result; } if (!ledger->hasaccount(rippleaddress)) return rpcerror(rpcact_not_found); std::string strpeer(params.ismember(jss::peer) ? params[jss::peer].asstring() : ""); bool bpeerindex(params.ismember(jss::peer_index)); int ipeerindex(bindex ? params[jss::peer_index].asuint() : 0); rippleaddress rippleaddresspeer; json::value const jvpeer(rpc::accountfromstring(ledger, rippleaddresspeer, bpeerindex, strpeer, ipeerindex, false, context.netops)); if (!jvpeer.empty()) { return result; } if (!ledger->hasaccount(rippleaddresspeer)) return rpcerror(rpcact_not_found); currency ucurrency; if (!to_currency(ucurrency, params[jss::currency].asstring())) return rpc::make_error(rpcsrc_cur_malformed, "invalid field 'currency', bad currency."); account const& raaccount(rippleaddress.getaccountid()); account const& rapeeraccount(rippleaddresspeer.getaccountid()); uint256 unodeindex = getripplestateindex(raaccount, rapeeraccount, ucurrency); auto slenode = context.netops.getslei(ledger, unodeindex); auto const line(ripplestate::makeitem(raaccount, slenode)); if (line == nullptr || raaccount != line->getaccountid() || rapeeraccount != line->getaccountidpeer()) return result; json::value jsonlines(json::arrayvalue); addline(jsonlines, *line, ledger); result[jss::lines] = jsonlines[0u]; json::value& jsonassetstates(result[jss::states] = json::arrayvalue); // get asset_states for currency asset. if (assetcurrency() == line->getbalance().getcurrency()) { auto lpledger = std::make_shared<ledger> (std::ref (*ledger), false); ledgerentryset les(lpledger, tapnone); auto sleripplestate = les.entrycache(ltripple_state, getripplestateindex(raaccount, rapeeraccount, assetcurrency())); les.assetrelease(raaccount, rapeeraccount, assetcurrency(), sleripplestate); uint256 baseindex = getassetstateindex(line->getaccountid(), line->getaccountidpeer(), assetcurrency()); uint256 assetstateindex = getqualityindex(baseindex); uint256 assetstateend = getqualitynext(assetstateindex); for (;;) { auto const& sle = les.entrycache(ltasset_state, assetstateindex); if (sle) { stamount amount = sle->getfieldamount(sfamount); stamount released = sle->getfieldamount(sfdeliveredamount); if (sle->getfieldaccount160(sfaccount) == line->getaccountidpeer()) { amount.negate(); released.negate(); } auto reserved = released ? amount - released : amount; json::value& state(jsonassetstates.append(json::objectvalue)); state[jss::date] = static_cast<json::uint>(getquality(assetstateindex)); state[jss::amount] = amount.gettext(); state[jss::reserve] = reserved.gettext(); } auto const nextassetstate( les.getnextledgerindex(assetstateindex, assetstateend)); if (nextassetstate.iszero()) break; assetstateindex = nextassetstate; } } context.loadtype = resource::feemediumburdenrpc; return result; }
// { // account: account, // ledger_index_min: ledger_index // optional, defaults to earliest // ledger_index_max: ledger_index, // optional, defaults to latest // binary: boolean, // optional, defaults to false // forward: boolean, // optional, defaults to false // limit: integer, // optional // marker: opaque // optional, resume previous query // } json::value doaccounttx (rpc::context& context) { auto& params = context.params; rippleaddress raaccount; int limit = params.ismember (jss::limit) ? params[jss::limit].asuint () : -1; bool bbinary = params.ismember ("binary") && params["binary"].asbool (); bool bforward = params.ismember ("forward") && params["forward"].asbool (); std::uint32_t uledgermin; std::uint32_t uledgermax; std::uint32_t uvalidatedmin; std::uint32_t uvalidatedmax; bool bvalidated = context.netops.getvalidatedrange ( uvalidatedmin, uvalidatedmax); if (!bvalidated) { // don't have a validated ledger range. return rpcerror (rpclgr_idxs_invalid); } if (!params.ismember ("account")) return rpcerror (rpcinvalid_params); if (!raaccount.setaccountid (params["account"].asstring ())) return rpcerror (rpcact_malformed); context.loadtype = resource::feemediumburdenrpc; if (params.ismember ("ledger_index_min") || params.ismember ("ledger_index_max")) { std::int64_t iledgermin = params.ismember ("ledger_index_min") ? params["ledger_index_min"].asint () : -1; std::int64_t iledgermax = params.ismember ("ledger_index_max") ? params["ledger_index_max"].asint () : -1; uledgermin = iledgermin == -1 ? uvalidatedmin : ((iledgermin >= uvalidatedmin) ? iledgermin : uvalidatedmin); uledgermax = iledgermax == -1 ? uvalidatedmax : ((iledgermax <= uvalidatedmax) ? iledgermax : uvalidatedmax); if (uledgermax < uledgermin) return rpcerror (rpclgr_idxs_invalid); } else { ledger::pointer l; json::value ret = rpc::lookupledger (params, l, context.netops); if (!l) return ret; uledgermin = uledgermax = l->getledgerseq (); } std::string txtype = ""; if (params.ismember("tx_type")) { txtype = params["tx_type"].asstring(); //check validation of tx_type, protect from sql injection try { txformats::getinstance().findtypebyname(txtype); } catch (...) { writelog (lswarning, accounttx) << "invalide tx_type " << txtype; txtype = ""; return rpcerror (rpcinvalid_params); } } json::value resumetoken; if (params.ismember(jss::marker)) resumetoken = params[jss::marker]; #ifndef beast_debug try { #endif json::value ret (json::objectvalue); ret["account"] = raaccount.humanaccountid (); json::value& jvtxns = (ret["transactions"] = json::arrayvalue); if (bbinary) { auto txns = context.netops.gettxsaccountb ( raaccount, uledgermin, uledgermax, bforward, resumetoken, limit, context.role == role::admin, txtype); for (auto& it: txns) { json::value& jvobj = jvtxns.append (json::objectvalue); jvobj["tx_blob"] = std::get<0> (it); jvobj["meta"] = std::get<1> (it); std::uint32_t uledgerindex = std::get<2> (it); jvobj["ledger_index"] = uledgerindex; jvobj[jss::validated] = bvalidated && uvalidatedmin <= uledgerindex && uvalidatedmax >= uledgerindex; } } else { auto txns = context.netops.gettxsaccount ( raaccount, uledgermin, uledgermax, bforward, resumetoken, limit, context.role == role::admin, txtype); for (auto& it: txns) { json::value& jvobj = jvtxns.append (json::objectvalue); if (it.first) jvobj[jss::tx] = it.first->getjson (1); if (it.second) { auto meta = it.second->getjson (1); addpaymentdeliveredamount (meta, context, it.first, it.second); jvobj[jss::meta] = meta; std::uint32_t uledgerindex = it.second->getlgrseq (); jvobj[jss::validated] = bvalidated && uvalidatedmin <= uledgerindex && uvalidatedmax >= uledgerindex; } } } //add information about the original query ret[jss::ledger_index_min] = uledgermin; ret[jss::ledger_index_max] = uledgermax; if (params.ismember (jss::limit)) ret[jss::limit] = limit; if (!resumetoken.isnull()) ret[jss::marker] = resumetoken; return ret; #ifndef beast_debug } catch (...) { return rpcerror (rpcinternal); } #endif }
// { // account: <account>|<account_public_key> // account_index: <number> // optional, defaults to 0. // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } json::value doaccountoffers (rpc::context& context) { auto const& params (context.params); if (! params.ismember (jss::account)) return rpc::missing_field_error ("account"); ledger::pointer ledger; json::value result (rpc::lookupledger (params, ledger, context.netops)); if (! ledger) return result; std::string strident (params[jss::account].asstring ()); bool bindex (params.ismember (jss::account_index)); int const iindex (bindex ? params[jss::account_index].asuint () : 0); rippleaddress rippleaddress; json::value const jv (rpc::accountfromstring (ledger, rippleaddress, bindex, strident, iindex, false, context.netops)); if (! jv.empty ()) { for (json::value::const_iterator it (jv.begin ()); it != jv.end (); ++it) result[it.membername ()] = it.key (); return result; } // get info on account. result[jss::account] = rippleaddress.humanaccountid (); if (bindex) result[jss::account_index] = iindex; if (! ledger->hasaccount (rippleaddress)) return rpcerror (rpcact_not_found); unsigned int limit; if (params.ismember (jss::limit)) { auto const& jvlimit (params[jss::limit]); if (! jvlimit.isintegral ()) return rpc::expected_field_error ("limit", "unsigned integer"); limit = jvlimit.isuint () ? jvlimit.asuint () : std::max (0, jvlimit.asint ()); if (context.role != role::admin) { limit = std::max (rpc::tuning::minoffersperrequest, std::min (limit, rpc::tuning::maxoffersperrequest)); } } else { limit = rpc::tuning::defaultoffersperrequest; } account const& raaccount (rippleaddress.getaccountid ()); json::value& jsonoffers (result[jss::offers] = json::arrayvalue); std::vector <sle::pointer> offers; unsigned int reserve (limit); uint256 startafter; std::uint64_t starthint; if (params.ismember(jss::marker)) { // we have a start point. use limit - 1 from the result and use the // very last one for the resume. json::value const& marker (params[jss::marker]); if (! marker.isstring ()) return rpc::expected_field_error ("marker", "string"); startafter.sethex (marker.asstring ()); sle::pointer sleoffer (ledger->getslei (startafter)); if (sleoffer == nullptr || sleoffer->gettype () != ltoffer || raaccount != sleoffer->getfieldaccount160 (sfaccount)) { return rpcerror (rpcinvalid_params); } starthint = sleoffer->getfieldu64(sfownernode); // caller provided the first offer (startafter), add it as first result json::value& obj (jsonoffers.append (json::objectvalue)); sleoffer->getfieldamount (sftakerpays).setjson (obj[jss::taker_pays]); sleoffer->getfieldamount (sftakergets).setjson (obj[jss::taker_gets]); obj[jss::seq] = sleoffer->getfieldu32 (sfsequence); obj[jss::flags] = sleoffer->getfieldu32 (sfflags); offers.reserve (reserve); } else { starthint = 0; // we have no start point, limit should be one higher than requested. offers.reserve (++reserve); } if (! ledger->visitaccountitems (raaccount, startafter, starthint, reserve, [&offers](sle::ref offer) { if (offer->gettype () == ltoffer) { offers.emplace_back (offer); return true; } return false; })) { return rpcerror (rpcinvalid_params); } if (offers.size () == reserve) { result[jss::limit] = limit; result[jss::marker] = to_string (offers.back ()->getindex ()); offers.pop_back (); } for (auto const& offer : offers) { json::value& obj (jsonoffers.append (json::objectvalue)); offer->getfieldamount (sftakerpays).setjson (obj[jss::taker_pays]); offer->getfieldamount (sftakergets).setjson (obj[jss::taker_gets]); obj[jss::seq] = offer->getfieldu32 (sfsequence); obj[jss::flags] = offer->getfieldu32 (sfflags); } context.loadtype = resource::feemediumburdenrpc; return result; }
void syncfinish(Syncpath *s) { int iscomplete; Vtime *m; /* * We need to figure out whether a and b are now * up to date with respect to the other. This state is * stored in s->a.complete and s->b.complete. * If a or b was complete or incomplete without * doing any work, these are already set. Else they are -1. * * If the sync failed, we map -1 to 0. * If the sync succeeded, we map -1 to 1. * * If the sync was one-way only, then the value * of s->a.complete is incorrect but will not be used. */ iscomplete = (s->state == SyncDone); if(nop){ s->a.complete = 0; s->b.complete = 0; } dbg(DbgSync, "syncfinish %P %d %d %d\n", s->p, iscomplete, s->a.complete, s->b.complete); if(s->a.complete == -1) s->a.complete = iscomplete; if(s->b.complete == -1) s->b.complete = iscomplete; dbg(DbgSync, "syncfinish %P %d %d\n", s->p, s->a.complete, s->b.complete); /* * Update the sync time on the now synced systems. * For a directory, propagate the mtime too. * * BUG: I think that when we create a directory tree * (meaning one of the ->state's is not SDir), * we might not set the mtime properly. Check this. */ if(s->b.complete){ m = nil; /* BUG: why is this both instead of just one? */ if(s->a.s->state==SDir && s->b.s->state==SDir) m = s->a.s->mtime; if(rpcaddtime(s->sync->rb, s->p, s->a.s->synctime, m) < 0){ s->state = SyncError; s->err = rpcerror(); s->b.complete = 0; s->a.complete = 0; goto Err; } } if(!s->sync->oneway && s->a.complete){ m = nil; if(s->b.s->state==SDir && s->a.s->state==SDir) m = s->b.s->mtime; if(rpcaddtime(s->sync->ra, s->p, s->b.s->synctime, m) < 0){ s->state = SyncError; s->err = rpcerror(); s->a.complete = 0; goto Err; } } Err: qsend(s->sync->finishq, s); }