]> sourceware.org Git - systemtap.git/blob - client-http.cxx
530bdac1775f73819ed04892b183912f1dd27d25
[systemtap.git] / client-http.cxx
1 // -*- C++ -*-
2 // Copyright (C) 2017 Red Hat Inc.
3 //
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
7 // later version.
8
9 #include "config.h"
10
11 #ifdef HAVE_HTTP_SUPPORT
12 #include "session.h"
13 #include "client-http.h"
14 #include "util.h"
15
16 #include <iostream>
17 #include <sstream>
18 #include <fstream>
19
20 extern "C" {
21 #include <string.h>
22 #include <curl/curl.h>
23 #include <curl/easy.h>
24 #include <curl/curlbuild.h>
25 #include <sys/stat.h>
26 }
27 using namespace std;
28
29
30 size_t
31 http_client_backend::get_data (void *ptr, size_t size, size_t nitems,
32 http_client_backend * client)
33 {
34 string data ((const char *) ptr, (size_t) size * nitems);
35
36 if (data.front () == '{')
37 {
38 Json::Reader reader;
39 bool parsedSuccess = reader.parse (data,
40 client->root,
41 false);
42 if (not parsedSuccess)
43 clog << "Failed to parse JSON" << reader.getFormattedErrorMessages() << endl;
44 }
45 return size * nitems;
46 }
47
48 size_t
49 http_client_backend::get_header (void *ptr, size_t size, size_t nitems,
50 http_client_backend * client)
51 {
52 string data ((const char *) ptr, (size_t) size * nitems);
53
54 unsigned long colon = data.find(':');
55 if (colon != string::npos)
56 {
57 string key = data.substr (0, colon);
58 string value = data.substr (colon + 2, data.length() - colon - 4);
59 client->header_values[key] = value;
60 }
61
62 return size * nitems;
63 }
64
65 size_t
66 http_client_backend::get_file (void *ptr, size_t size, size_t nitems,
67 std::FILE * stream)
68 {
69 size_t written;
70 written = fwrite(ptr, size, nitems, stream);
71 std::fflush(stream);
72 return written;
73 }
74
75 bool
76 http_client_backend::download (const std::string & url, http_client_backend::download_type type)
77 {
78 struct curl_slist *headers = NULL;
79
80 curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
81 /* example.com is redirected, so we tell libcurl to follow redirection */
82 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
83 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
84 curl_easy_setopt (curl, CURLOPT_ACCEPT_ENCODING, "deflate");
85 headers = curl_slist_append (headers, "Accept: */*");
86 headers = curl_slist_append (headers, "Content-Type: text/html");
87 curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
88 curl_easy_setopt (curl, CURLOPT_HTTPGET, 1);
89
90 static void *
91 lthis = this;
92 if (type == json_type)
93 {
94 curl_easy_setopt (curl, CURLOPT_WRITEDATA, lthis);
95 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, http_client_backend::get_data);
96 }
97 else if (type == file_type)
98 {
99 std::string filename = url;
100 std::string ko_suffix = ".ko";
101 std::string filepath;
102 if (filename.back() == '/')
103 filename.erase(filename.length()-1);
104
105 if (std::equal(ko_suffix.rbegin(), ko_suffix.rend(), filename.rbegin()))
106 filepath = s.tmpdir + "/" + s.module_name + ".ko";
107 else
108 filepath = s.tmpdir + "/" + filename.substr (filename.rfind ('/')+1);
109
110 if (s.verbose >= 3)
111 clog << "Downloaded " + filepath << endl;
112 std::FILE *File = std::fopen(filepath.c_str(), "wb");
113 curl_easy_setopt (curl, CURLOPT_WRITEDATA, File);
114 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, http_client_backend::get_file);
115 }
116 curl_easy_setopt (curl, CURLOPT_HEADERDATA, lthis);
117 curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, http_client_backend::get_header);
118
119 CURLcode
120 res = curl_easy_perform (curl);
121
122 if (res != CURLE_OK)
123 {
124 fprintf (stderr, "curl_easy_perform() failed in %s: %s\n",
125 __FUNCTION__, curl_easy_strerror (res));
126 return false;
127
128 }
129 else
130 return true;
131 }
132
133 void
134 http_client_backend::post (const std::string & url, const std::string & data)
135 {
136 struct curl_slist *headers = NULL;
137
138 curl_easy_setopt (curl, CURLOPT_URL, url.c_str());
139 curl_easy_setopt (curl, CURLOPT_POST, 1);
140 curl_easy_setopt (curl, CURLOPT_POSTFIELDS, data.c_str ());
141 headers = curl_slist_append (headers, "Accept: */*");
142 headers =
143 curl_slist_append (headers,
144 "Content-Type: application/x-www-form-urlencoded");
145 curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
146
147 CURLcode res = curl_easy_perform (curl);
148 /* Check for errors */
149 if (res != CURLE_OK)
150 {
151 fprintf (stderr, "curl_easy_perform() failed in %s: %s\n",
152 __FUNCTION__, curl_easy_strerror (res));
153 }
154 }
155
156
157 int
158 http_client_backend::initialize ()
159 {
160 request_parameters.clear();
161 request_files.clear();
162 curl = curl_easy_init ();
163 retry = 0;
164 location = nullptr;
165 return 0;
166 }
167
168 int
169 http_client_backend::package_request ()
170 {
171 const std::string query_parts [] =
172 {"kver=",
173 "&arch="
174 };
175 struct stat buffer;
176
177 query = query_parts[0] + s.release + query_parts[1] + s.release.substr (s.release.rfind ('.')+1);
178
179 for (vector<std::tuple<std::string, std::string>>::const_iterator it = request_parameters.begin ();
180 it != request_parameters.end ();
181 ++it)
182 {
183 string parm_type = get<0>(*it);
184 if (parm_type == "cmd_args" && stat (get<1>(*it).c_str(), &buffer) != 0)
185 query = query + "&cmd_args=" + get<1>(*it);
186 }
187
188 if (s.verbose >= 3)
189 clog << "Query: " + query << endl;
190
191 return 0;
192 }
193
194 int
195 http_client_backend::find_and_connect_to_server ()
196 {
197 for (vector<std::string>::const_iterator i = s.http_servers.begin ();
198 i != s.http_servers.end ();
199 ++i)
200 if (download (*i + "/builds", json_type))
201 {
202 host = *i;
203 post (host + "/builds", query);
204 }
205
206 std::string::size_type found = host.find ("/builds");
207 std::string uri;
208 if (found != std::string::npos)
209 uri = host.substr (0, found) + header_values["Location"];
210 else
211 uri = host + header_values["Location"];
212
213 while (true)
214 {
215 int retry = std::stoi (header_values["Retry-After"], nullptr, 10);
216 if (s.verbose >= 2)
217 clog << "Waiting " << retry << " seconds...";
218 sleep (retry);
219 if (download (host + header_values["Location"], json_type))
220 {
221 const Json::Value uuid = root["uuid"];
222
223 for(Json::Value::iterator it = root["files"].begin();
224 it != root["files"].end(); ++it)
225
226 for(unsigned int index=0; index < (*it).size()-1;
227 ++index)
228 download (host + (*it)["location"].asString(), file_type);
229
230 if (s.verbose >= 5)
231 for(Json::Value::iterator it = root.begin();
232 it != root.end(); ++it)
233 {
234 Json::Value val = (*it);
235 std::cout << it.key().asString() << ':' << (*it) << '\n';
236 }
237 break;
238 }
239 }
240
241 download (host + root["stderr_location"].asString(), file_type);
242 std::ifstream ferr(s.tmpdir + "/stderr");
243 if (ferr.is_open())
244 std::cout << ferr.rdbuf() << endl;
245 ferr.close();
246 download (host + root["stdout_location"].asString(), file_type);
247 std::ifstream fout(s.tmpdir + "/stdout");
248 if (fout.is_open())
249 std::cout << fout.rdbuf() << endl;
250 fout.close();
251
252 return 0;
253 }
254
255 int
256 http_client_backend::unpack_response ()
257 {
258 return 0;
259 }
260
261 int
262 http_client_backend::process_response ()
263 {
264 return 0;
265 }
266
267 int
268 http_client_backend::add_protocol_version (const std::string &version)
269 {
270 // Add the protocol version (so the server can ensure we're
271 // compatible).
272 request_parameters.push_back(make_tuple("version", version));
273 return 0;
274 }
275
276 int
277 http_client_backend::add_sysinfo ()
278 {
279 // Add the sysinfo.
280 request_parameters.push_back(make_tuple("kver", s.kernel_release));
281 request_parameters.push_back(make_tuple("arch", s.architecture));
282 return 0;
283 }
284
285 int
286 http_client_backend::include_file_or_directory (const std::string &,
287 const std::string &)
288 {
289 // FIXME: this is going to be interesting. We can't add a whole
290 // directory at one shot, we'll have to traverse the directory and
291 // add each file, preserving the directory structure somehow.
292 return 0;
293 }
294
295 int
296 http_client_backend::add_tmpdir_file (const std::string &file)
297 {
298 request_files.push_back(make_tuple("files", file));
299 return 0;
300 }
301
302 int
303 http_client_backend::add_cmd_arg (const std::string &arg)
304 {
305 request_parameters.push_back(make_tuple("cmd_args", arg));
306 return 0;
307 }
308
309 void
310 http_client_backend::add_localization_variable(const std::string &,
311 const std::string &)
312 {
313 // FIXME: We'll probably just add to the request_parameters here.
314 return;
315 }
316
317 void
318 http_client_backend::add_mok_fingerprint(const std::string &)
319 {
320 // FIXME: We'll probably just add to the request_parameters here.
321 return;
322 }
323
324 #endif /* HAVE_HTTP_SUPPORT */
This page took 0.048393 seconds and 5 git commands to generate.