GCC Code Coverage Report


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