| // Copyright (c) 2013 The Chromium OS Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <signal.h> | 
 | #include <stdint.h> | 
 | #include <stdio.h> | 
 |  | 
 | #include <cassert> | 
 | #include <cerrno> | 
 | #include <memory> | 
 |  | 
 | #include <base/bind.h> | 
 | #include <base/command_line.h> | 
 | #include <base/logging.h> | 
 | #include <base/rand_util.h> | 
 | #include <base/strings/string_number_conversions.h> | 
 | #include <metrics/metrics_library.h> | 
 |  | 
 | #include "p2p/client/peer_selector.h" | 
 | #include "p2p/client/service_finder.h" | 
 | #include "p2p/common/clock.h" | 
 | #include "p2p/common/constants.h" | 
 | #include "p2p/common/util.h" | 
 |  | 
 | using std::map; | 
 | using std::string; | 
 | using std::vector; | 
 |  | 
 | /* Global pointer to the PeerSelector being used. Only used from the signal | 
 |  * handler of SIGTERM. */ | 
 | static p2p::client::PeerSelector* volatile global_peer_selector = NULL; | 
 |  | 
 | static void sigterm_handler(int signum) { | 
 |   /* This function is non-reentrant since is only used to handle SIGTERM. | 
 |    * A second SIGTERM signal will wait until this call finishes. */ | 
 |   if (global_peer_selector) | 
 |     global_peer_selector->Abort(); | 
 | } | 
 |  | 
 | static void Usage(FILE* output) { | 
 |   fprintf(output, | 
 |           "Usage:\n" | 
 |           "  p2p-client [OPTION..]\n" | 
 |           "\n" | 
 |           "Options:\n" | 
 |           " --help             Show help options\n" | 
 |           " --list-all         Scan network and list available files\n" | 
 |           " --list-urls=ID     Like --list-all but only show peers for ID\n" | 
 |           " --get-url=ID       Scan for ID and pick a suitable peer\n" | 
 |           " --num-connections  Show total number of connections in the LAN\n" | 
 |           " -v=NUMBER          Verbosity level (default: 0)\n" | 
 |           " --minimum-size=NUM When used with --get-url, scans for files\n" | 
 |           "                    with at least NUM bytes (default: 1).\n" | 
 |           "\n"); | 
 | } | 
 |  | 
 | // Lists all URLs discovered via |finder|. If |id| is not the empty | 
 | // string then only lists URLs matching it. | 
 | static void ListUrls(p2p::client::ServiceFinder* finder, | 
 |                      const std::string& id) { | 
 |   vector<string> files = finder->AvailableFiles(); | 
 |  | 
 |   for (auto const& file_name : files) { | 
 |     if (id == "" || file_name == id) { | 
 |       printf("%s\n", file_name.c_str()); | 
 |       vector<const p2p::client::Peer*> peers = | 
 |           finder->GetPeersForFile(file_name); | 
 |       for (auto const& peer : peers) { | 
 |         map<string, size_t>::const_iterator file_size_it = | 
 |             peer->files.find(file_name); | 
 |         printf(" address %s, port %d, size %zu, num_connections %d\n", | 
 |                peer->address.c_str(), peer->port, | 
 |                (file_size_it == peer->files.end() ? -1 : file_size_it->second), | 
 |                peer->num_connections); | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | int main(int argc, char* argv[]) { | 
 |   std::unique_ptr<p2p::client::ServiceFinder> finder; | 
 |  | 
 |   base::CommandLine::Init(argc, argv); | 
 |   logging::LoggingSettings logging_settings; | 
 |   logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; | 
 |   logging_settings.lock_log = logging::LOCK_LOG_FILE; | 
 |   logging_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE; | 
 |   logging::InitLogging(logging_settings); | 
 |   p2p::util::SetupSyslog("p2p-client", true /* include_pid */); | 
 |  | 
 |   base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); | 
 |  | 
 |   // If help is requested, show usage and exit immediately | 
 |   if (cl->HasSwitch("help")) { | 
 |     Usage(stdout); | 
 |     return 0; | 
 |   } | 
 |  | 
 |   // Get us a ServiceFinder and look up all peers - this takes a couple | 
 |   // of seconds. This can fail if e.g. avahi-daemon is not running. | 
 |   finder.reset(p2p::client::ServiceFinder::Construct()); | 
 |   if (finder == NULL) | 
 |     return 1; | 
 |  | 
 |   p2p::common::Clock clock; | 
 |   p2p::client::PeerSelector peer_selector(finder.get(), &clock); | 
 |   // The Metrics Library interface for reporting UMA stats. | 
 |  | 
 |   if (cl->HasSwitch("list-all")) { | 
 |     finder->Lookup(); | 
 |     ListUrls(finder.get(), ""); | 
 |   } else if (cl->HasSwitch("num-connections")) { | 
 |     finder->Lookup(); | 
 |     int num_connections = finder->NumTotalConnections(); | 
 |     printf("%d\n", num_connections); | 
 |   } else if (cl->HasSwitch("get-url")) { | 
 |     string id = cl->GetSwitchValueNative("get-url"); | 
 |     uint64_t minimum_size = 1; | 
 |     if (cl->HasSwitch("minimum-size")) { | 
 |       string minimum_size_str = cl->GetSwitchValueNative("minimum-size"); | 
 |       if (!base::StringToUint64(minimum_size_str, &minimum_size)) { | 
 |         LOG(ERROR) << "Invalid --minimum-size argument"; | 
 |         return 1; | 
 |       } | 
 |     } | 
 |  | 
 |     // Register the SIGTERM signal handler in order to abort the | 
 |     // GetUrlAndWait() call, but reporting the metric. | 
 |     global_peer_selector = &peer_selector; | 
 |     signal(SIGTERM, sigterm_handler); | 
 |  | 
 |     string url = peer_selector.GetUrlAndWait(id, minimum_size); | 
 |  | 
 |     // Remove the global pointer reference to avoid a Abort() call due a | 
 |     // SIGTERM after the pointed object is destroyed. | 
 |     global_peer_selector = NULL; | 
 |  | 
 |     // Report the metrics. | 
 |     MetricsLibrary metrics_lib; | 
 |     peer_selector.ReportMetrics(&metrics_lib); | 
 |  | 
 |     if (url == "") | 
 |       return 1; | 
 |     printf("%s\n", url.c_str()); | 
 |   } else if (cl->HasSwitch("list-urls")) { | 
 |     string id = cl->GetSwitchValueNative("list-urls"); | 
 |     finder->Lookup(); | 
 |     ListUrls(finder.get(), id); | 
 |   } else { | 
 |     Usage(stderr); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } |