GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/notify/cmd_sub.cc
Date: 2026-08-30 02:40:36
Exec Total Coverage
Lines: 0 64 0.0%
Branches: 0 38 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "cmd_sub.h"
6
7 #include <inttypes.h>
8
9 #include <memory>
10
11 #include "crypto/signature.h"
12 #include "manifest.h"
13 #include "manifest_fetch.h"
14 #include "network/download.h"
15 #include "notify/messages.h"
16 #include "options.h"
17 #include "subscriber_sse.h"
18 #include "subscriber_supervisor.h"
19 #include "supervisor.h"
20 #include "util/logging.h"
21 #include "util/posix.h"
22 #include "util/string.h"
23
24 namespace {
25
26 const LogFacilities &kLogInfo = DefaultLogging::info;
27 const LogFacilities &kLogError = DefaultLogging::error;
28
29 const int kMaxPoolHandles = 1;
30
31 class SwissknifeSubscriber : public notify::SubscriberSSE {
32 public:
33 SwissknifeSubscriber(const std::string &server_url,
34 const std::string &repository, uint64_t min_revision,
35 bool continuous, bool verbose)
36 : notify::SubscriberSSE(server_url)
37 , repository_(repository)
38 , stats_()
39 , dl_mgr_(new download::DownloadManager(
40 kMaxPoolHandles, perf::StatisticsTemplate("download", &stats_)))
41 , sig_mgr_(new signature::SignatureManager())
42 , revision_(min_revision)
43 , continuous_(continuous)
44 , verbose_(verbose) { }
45 virtual ~SwissknifeSubscriber() { sig_mgr_->Fini(); }
46
47 bool Init() {
48 const std::string config_file = "/etc/cvmfs/repositories.d/" + repository_
49 + "/client.conf";
50 SimpleOptionsParser options;
51 if (!options.TryParsePath(config_file)) {
52 LogCvmfs(kLogCvmfs, kLogError,
53 "SwissknifeSubscriber - could not parse configuration file");
54 return false;
55 }
56
57 std::string arg;
58 if (options.GetValue("CVMFS_SERVER_URL", &arg)) {
59 dl_mgr_->SetHostChain(arg);
60 }
61
62 sig_mgr_->Init();
63
64 const std::string public_keys = JoinStrings(
65 FindFilesBySuffix("/etc/cvmfs/keys", ".pub"), ":");
66 if (!sig_mgr_->LoadPublicRsaKeys(public_keys)) {
67 LogCvmfs(kLogCvmfs, kLogError,
68 "SwissknifeSubscriber - could not load public keys");
69 return false;
70 }
71
72 return true;
73 }
74
75 private:
76 virtual notify::Subscriber::Status Consume(const std::string &repo,
77 const std::string &msg_text) {
78 notify::msg::Activity msg;
79 if (!msg.FromJSONString(msg_text)) {
80 LogCvmfs(kLogCvmfs, kLogError,
81 "SwissknifeSubscriber - could not decode message.");
82 return notify::Subscriber::kError;
83 }
84
85 manifest::ManifestEnsemble ensemble;
86 const manifest::Failures res = manifest::Verify(
87 reinterpret_cast<unsigned char *>(&(msg.manifest_[0])),
88 msg.manifest_.size(), "", repo, 0, NULL, sig_mgr_.get(), dl_mgr_.get(),
89 &ensemble);
90
91 if (res != manifest::kFailOk) {
92 LogCvmfs(kLogCvmfs, kLogError,
93 "SwissknifeSubscriber - manifest has invalid signature: %d",
94 res);
95 return notify::Subscriber::kError;
96 }
97
98 const std::unique_ptr<manifest::Manifest> manifest(
99 manifest::Manifest::LoadMem(
100 reinterpret_cast<const unsigned char *>(msg.manifest_.data()),
101 msg.manifest_.size()));
102
103 if (manifest.get() == nullptr) {
104 LogCvmfs(kLogCvmfs, kLogError,
105 "SwissknifeSubscriber - could not parse manifest.");
106 return notify::Subscriber::kError;
107 }
108
109 const uint64_t new_revision = manifest->revision();
110 bool triggered = false;
111 if (new_revision > revision_) {
112 LogCvmfs(
113 kLogCvmfs, kLogInfo,
114 "SwissknifeSubscriber - repository %s is now at revision %" PRIu64
115 ".",
116 repo.c_str(), new_revision);
117 if (verbose_) {
118 LogCvmfs(kLogCvmfs, kLogInfo, "%s", msg_text.c_str());
119 }
120 revision_ = new_revision;
121 triggered = true;
122 }
123
124 if (!continuous_ && triggered) {
125 return notify::Subscriber::kFinish;
126 }
127
128 return notify::Subscriber::kContinue;
129 }
130
131 std::string repository_;
132
133 perf::Statistics stats_;
134 std::unique_ptr<download::DownloadManager> dl_mgr_;
135 std::unique_ptr<signature::SignatureManager> sig_mgr_;
136
137 uint64_t revision_;
138 bool continuous_;
139 bool verbose_;
140 };
141
142 } // namespace
143
144 namespace notify {
145
146 int DoSubscribe(const std::string &server_url, const std::string &repo,
147 uint64_t min_revision, bool continuous, bool verbose) {
148 SwissknifeSubscriber subscriber(server_url, repo, min_revision, continuous,
149 verbose);
150
151 if (!subscriber.Init()) {
152 LogCvmfs(kLogCvmfs, kLogError, "Could not initialize SwissknifeSubscriber");
153 return 1;
154 }
155
156 // Retry settings: accept no more than 10 failures in the last minute
157 const int num_retries = 10;
158 const uint64_t interval = 60;
159 SubscriberSupervisor supervisor(&subscriber, repo, num_retries, interval);
160 supervisor.Run();
161
162 return 0;
163 }
164
165 } // namespace notify
166