GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/swissknife_lease_curl.cc
Date: 2025-09-21 02:35:44
Exec Total Coverage
Lines: 0 94 0.0%
Branches: 0 188 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "swissknife_lease_curl.h"
6
7 #include <unistd.h>
8
9 #include "crypto/hash.h"
10 #include "gateway_util.h"
11 #include "json_document.h"
12 #include "json_document_write.h"
13 #include "ssl.h"
14 #include "util/logging.h"
15 #include "util/pointer.h"
16 #include "util/posix.h"
17 #include "util/string.h"
18
19 long g_final_revision = -1;
20
21 namespace {
22
23 CURL *PrepareCurl(const std::string &method) {
24 const char *user_agent_string = "cvmfs/" CVMFS_VERSION;
25
26 CURL *h_curl = curl_easy_init();
27
28 if (h_curl) {
29 curl_easy_setopt(h_curl, CURLOPT_NOPROGRESS, 1L);
30 curl_easy_setopt(h_curl, CURLOPT_USERAGENT, user_agent_string);
31 curl_easy_setopt(h_curl, CURLOPT_MAXREDIRS, 50L);
32 curl_easy_setopt(h_curl, CURLOPT_CUSTOMREQUEST, method.c_str());
33 }
34
35 return h_curl;
36 }
37
38 size_t RecvCB(void *buffer, size_t size, size_t nmemb, void *userp) {
39 CurlBuffer *my_buffer = static_cast<CurlBuffer *>(userp);
40
41 if (size * nmemb < 1) {
42 return 0;
43 }
44
45 my_buffer->data = my_buffer->data
46 + std::string(static_cast<char *>(buffer), nmemb);
47
48 return nmemb;
49 }
50
51 } // namespace
52
53 bool MakeAcquireRequest(const std::string &key_id, const std::string &secret,
54 const std::string &repo_path,
55 const std::string &repo_service_url, CurlBuffer *buffer,
56 const std::string &metadata) {
57 CURLcode ret = static_cast<CURLcode>(0);
58
59 CURL *h_curl = PrepareCurl("POST");
60 if (!h_curl) {
61 return false;
62 }
63
64 JsonStringGenerator payloadJson;
65 payloadJson.Add("path", repo_path);
66 payloadJson.Add("api_version", StringifyInt(gateway::APIVersion()));
67 payloadJson.Add("hostname", GetHostname());
68 if (!metadata.empty()) {
69 payloadJson.AddJsonObject("metadata", metadata);
70 }
71 const std::string payload = payloadJson.GenerateString();
72
73 shash::Any hmac(shash::kSha1);
74 shash::HmacString(secret, payload, &hmac);
75
76 SslCertificateStore cs;
77 cs.UseSystemCertificatePath();
78 cs.ApplySslCertificatePath(h_curl);
79
80 const std::string header_str = std::string("Authorization: ") + key_id + " "
81 + Base64(hmac.ToString(false));
82 struct curl_slist *auth_header = NULL;
83 auth_header = curl_slist_append(auth_header, header_str.c_str());
84 curl_easy_setopt(h_curl, CURLOPT_HTTPHEADER, auth_header);
85
86 // Make request to acquire lease from repo services
87 curl_easy_setopt(h_curl, CURLOPT_URL, (repo_service_url + "/leases").c_str());
88 curl_easy_setopt(h_curl, CURLOPT_POSTFIELDSIZE_LARGE,
89 static_cast<curl_off_t>(payload.length()));
90 curl_easy_setopt(h_curl, CURLOPT_POSTFIELDS, payload.c_str());
91 curl_easy_setopt(h_curl, CURLOPT_WRITEFUNCTION, RecvCB);
92 curl_easy_setopt(h_curl, CURLOPT_WRITEDATA, buffer);
93
94 ret = curl_easy_perform(h_curl);
95 if (ret) {
96 LogCvmfs(kLogUploadGateway, kLogStderr,
97 "Make lease acquire request failed: %d. Reply: %s", ret,
98 buffer->data.c_str());
99 }
100
101 curl_easy_cleanup(h_curl);
102 h_curl = NULL;
103
104 return !ret;
105 }
106
107 bool MakeEndRequest(const std::string &method, const std::string &key_id,
108 const std::string &secret, const std::string &session_token,
109 const std::string &repo_service_url,
110 const std::string &request_payload, CurlBuffer *reply,
111 bool expect_final_revision) {
112 CURLcode ret = static_cast<CURLcode>(0);
113
114 CURL *h_curl = PrepareCurl(method);
115 if (!h_curl) {
116 return false;
117 }
118
119 shash::Any hmac(shash::kSha1);
120 shash::HmacString(secret, session_token, &hmac);
121
122 SslCertificateStore cs;
123 cs.UseSystemCertificatePath();
124 cs.ApplySslCertificatePath(h_curl);
125
126 const std::string header_str = std::string("Authorization: ") + key_id + " "
127 + Base64(hmac.ToString(false));
128 struct curl_slist *auth_header = NULL;
129 auth_header = curl_slist_append(auth_header, header_str.c_str());
130 curl_easy_setopt(h_curl, CURLOPT_HTTPHEADER, auth_header);
131
132 curl_easy_setopt(h_curl, CURLOPT_URL,
133 (repo_service_url + "/leases/" + session_token).c_str());
134 if (request_payload != "") {
135 curl_easy_setopt(h_curl, CURLOPT_POSTFIELDSIZE_LARGE,
136 static_cast<curl_off_t>(request_payload.length()));
137 curl_easy_setopt(h_curl, CURLOPT_POSTFIELDS, request_payload.c_str());
138 } else {
139 curl_easy_setopt(h_curl, CURLOPT_POSTFIELDSIZE_LARGE,
140 static_cast<curl_off_t>(0));
141 curl_easy_setopt(h_curl, CURLOPT_POSTFIELDS, NULL);
142 }
143 curl_easy_setopt(h_curl, CURLOPT_WRITEFUNCTION, RecvCB);
144 curl_easy_setopt(h_curl, CURLOPT_WRITEDATA, reply);
145
146 ret = curl_easy_perform(h_curl);
147 if (ret) {
148 LogCvmfs(kLogUploadGateway, kLogStderr,
149 "Lease end request - curl_easy_perform failed: %d", ret);
150 }
151
152 JsonDocument *doc = JsonDocument::Create(reply->data);
153 bool ok = true;
154 if (!doc) {
155 ok = false;
156 } else {
157 UniquePtr<JsonDocument> const reply_json(doc);
158 const JSON *reply_status = JsonDocument::SearchInObject(
159 reply_json->root(), "status", JSON_STRING);
160 ok = (reply_status != NULL
161 && std::string(reply_status->string_value) == "ok");
162 if (!ok) {
163 LogCvmfs(kLogUploadGateway, kLogStderr,
164 "Lease end request - error reply: %s", reply->data.c_str());
165 }
166 if (expect_final_revision) {
167 const JSON *reply_final_rev = JsonDocument::SearchInObject(
168 reply_json->root(), "final_revision", JSON_INT);
169 ok = (reply_final_rev != NULL);
170 if (ok) {
171 g_final_revision = reply_final_rev->int_value;
172 } else {
173 g_final_revision = -1;
174 }
175 }
176 }
177
178 curl_easy_cleanup(h_curl);
179 h_curl = NULL;
180
181 return ok && !ret;
182 }
183