GCC Code Coverage Report


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