libadc-cxx 1.0.0
Structured logging for scientific computing
Loading...
Searching...
No Matches
curl.ipp
Go to the documentation of this file.
1/* Copyright 2025 NTESS. See the top-level LICENSE.txt file for details.
2 *
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
7#include <cstdlib>
8#include <iostream>
9#include <fstream>
10#include <filesystem>
11#include <unistd.h>
12
13namespace adc {
14
15using std::cout;
16
17typedef std::string string;
18typedef std::string_view string_view;
19
20/*! \brief Curl utility publisher_api implementation.
21 This plugin generates a scratch file (in-memory) and asynchronously
22 sends it to the configured web service by invoking the 'curl' utility.
23 Multiple independent instances of this plugin may be used simultaneously.
24 */
25class curl_plugin : public publisher_api {
26 enum state {
27 ok,
28 err
29 };
30 enum mode {
31 /* the next mode needed for correct operation */
32 pi_config,
33 pi_init,
34 pi_pub_or_final
35 };
36
37public:
38 /// \brief default scratch directory for curl utility messages.
39 /// This directory must be globally writable and should be fast.
40 /// Overridden with env("ADC_CURL_PLUGIN_DIRECTORY").
41 inline static const char *adc_curl_plugin_directory_default = "/dev/shm/adc";
42
43 /// \brief default full path to curl utility
44 /// This program should be world accessible.
45 /// Overridden with env("ADC_CURL_PLUGIN_PROG").
46 inline static const char *adc_curl_plugin_prog_default = "/usr/bin/curl";
47
48 /// \brief default ADC server https port.
49 /// The server should be highly available.
50 /// Overridden with env("ADC_CURL_PLUGIN_PORT").
51 inline static const char *adc_curl_plugin_port_default = "443";
52
53 /// \brief ADC data ingest server host.
54 /// The server should be highly available; the
55 /// default value of https://localhost must be overridden by an
56 /// environment variable unless the test environment includes a test server.
57 /// Overridden with env("ADC_CURL_PLUGIN_URL").
58 inline static const char *adc_curl_plugin_url_default = "https://localhost";
59
60 /// \brief ADC curl plugin enable debug messages; (default "0": none)
61 /// "2" provides message send debugging
62 /// "1" provides lighter debugging
63 /// Overridden with env("ADC_CURL_PLUGIN_DEBUG").
64 inline static const char *adc_curl_plugin_debug_default = "0";
65
66private:
67 inline static const char *plugin_prefix = "ADC_CURL_PLUGIN_";
68 inline static const std::map< const string, const string > plugin_config_defaults =
69 { {"DIRECTORY", adc_curl_plugin_directory_default},
74 };
75
76 const string vers;
77 const std::vector<string> tags;
78 string fdir;
79 string prog;
80 string port;
81 string url;
82 int debug;
83 enum state state;
84 bool paused;
85 enum mode mode;
86
87 int config(const string& dir, string_view surl, string_view sport, string_view sprog, const string& sdebug) {
88 if (mode != pi_config)
89 return 2;
90
91 uid_t ui = geteuid();
92 fdir = dir + string("/") + std::to_string(ui);
93 port = sport;
94 url = surl;
95 prog = sprog;
96 mode = pi_init;
97 std::stringstream ss(sdebug);
98 ss >> debug;
99 if (debug < 0) {
100 debug = 0;
101 }
102 if (debug > 0) {
103 std::cout<< "curl plugin configured" <<std::endl;
104 }
105 return 0;
106 }
107
108 // find field in m, then with prefix in env, then the default.
109 const string get(const std::map< string, string >& m,
110 string field, string_view env_prefix) {
111 // fields not defined in config_defaults raise an exception.
112 auto it = m.find(field);
113 if (it != m.end()) {
114 return it->second;
115 }
116 string en = string(env_prefix) += field;
117 char *ec = getenv(en.c_str());
118 if (!ec) {
119 return plugin_config_defaults.at(field);
120 } else {
121 return string(ec);
122 }
123 }
124
125 string get_temp_file_name() {
126 string tplt = fdir + "/json-curl-msg-XXXXXX";
127 char * cstr = new char [tplt.length()+1];
128 std::strcpy (cstr, tplt.c_str());
129 int fd = mkstemp(cstr);
130 if (fd >= 0) {
131 string f(cstr);
132 delete[] cstr;
133 close(fd);
134 // current user now owns tempfile.
135 return f;
136 }
137 std::cout<< "mkstemp failed" <<std::endl;
138 delete[] cstr;
139 return "";
140 }
141
142 // invoke curl with json in f, then delete f.
143 // wrap this in a thread to make it non-blocking eventually.
144 int curl_send(string& f)
145 {
146 if (debug == 2) {
147 // do not suppress stderr/stdout from system call
148 string cmd = "(" + prog +
149 " -X POST -s -w \"\n%{http_code}\n\" -H \"Content-Type: application/json\" -d @"
150 + f + " " + url + " || /bin/rm -f " + f + ") &";
151 int err1 = std::system(cmd.c_str());
152 std::cout << "cmd:" << cmd << std::endl;
153 std::cout << err1 << std::endl;
154 std::cout << f << std::endl;
155 } else {
156 // suppress stderr/stdout from system call
157 string qcmd = "(" + prog +
158 " -X POST -s -w \"\n%{http_code}\n\" -H \"Content-Type: application/json\" -d @"
159 + f + " " + url +
160 " > /dev/null 2>&1 ; /bin/rm -f " + f + ") &";
161 int err2 = std::system(qcmd.c_str());
162 if (debug) {
163 std::cout << err2 << std::endl;
164 }
165 }
166 return 0;
167 }
168
169public:
170 curl_plugin() : vers("1.0.0") , state(ok), paused(false), mode(pi_config) { }
171
172 /*!
173 */
174 int publish(std::shared_ptr<builder_api> b) {
175 if (paused)
176 return 0;
177 if (state != ok)
178 return 1;
179 if (mode != pi_pub_or_final)
180 return 2;
181 // write to tmpfile, curl, and then delete
182 // this could be made fancy with a work queue in a later reimplementation.
183 // try for /dev/shm/adc for performance and fall back to tmpdir
184 string fname = get_temp_file_name();
185 if (debug) {
186 std::cout << "name: " << fname <<std::endl;
187 }
188 // open dump file
189 std::ofstream out(fname);
190 if (out.good()) {
191 out << b->serialize() << std::endl;
192 if (out.good()) {
193 if (debug) {
194 std::cout << "'curl' wrote" << std::endl;
195 }
196 out.close();
197 curl_send(fname); // send also removes file
198 return 0;
199 } else {
200 std::cout << "failed write" << std::endl;
201 return 1;
202 }
203 }
204 std::filesystem::remove(fname);
205 std::cout << "failed open " << fname << std::endl;
206 return 1;
207 }
208
209 /*!
210 */
211 int config(const std::map< std::string, std::string >& m) {
212 return config(m, plugin_prefix);
213 }
214
215 /*!
216 */
217 int config(const std::map< std::string, std::string >& m, string_view env_prefix) {
218 string d = get(m, "DIRECTORY", env_prefix);
219 string url = get(m, "URL", env_prefix);
220 string port = get(m, "PORT", env_prefix);
221 string prog = get(m, "PROG", env_prefix);
222 string sdebug = get(m, "DEBUG", env_prefix);
223 return config(d, url, port, prog, sdebug);
224 }
225
226 const std::map< const std::string, const std::string> & get_option_defaults() {
227 return plugin_config_defaults;
228 }
229
231 std::map <string, string >m;
232 // config if never config'd
233 if (!fdir.size())
234 config(m);
235 if (mode != pi_init) {
236 return 2;
237 }
238 if ( state == err ) {
239 std::cout << "curl plugin initialize found pre-existing error" << std::endl;
240 return 3;
241 }
242 std::error_code ec;
243 std::filesystem::create_directories(fdir, ec);
244 if (ec.value() != 0 && ec.value() != EEXIST ) {
245 state = err;
246 std::cout << "unable to create scratch directory for plugin 'curl'; "
247 << fdir << " : " << ec.message() << std::endl;
248 return ec.value();
249 } else {
250 if (debug) {
251 std::cout << "created " << fdir <<std::endl;
252 }
253 mode = pi_pub_or_final;
254 }
255 return 0;
256 }
257
258 void finalize() {
259 if (mode == pi_pub_or_final) {
260 state = ok;
261 paused = false;
262 mode = pi_config;
263 } else {
264 if (debug) {
265 std::cout << "curl plugin finalize on non-running plugin" << std::endl;
266 }
267 }
268 }
269
270 void pause() {
271 paused = true;
272 }
273
274 void resume() {
275 paused = false;
276 }
277
279 return "curl";
280 }
281
283 return vers;
284 }
285
287 if (debug) {
288 std::cout << "Destructing curl_plugin" << std::endl;
289 }
290 }
291};
292
293} // adc
Curl utility publisher_api implementation. This plugin generates a scratch file (in-memory) and async...
Definition curl.ipp:25
int config(const std::map< std::string, std::string > &m, string_view env_prefix)
Configure the plugin with the options given and the corresponding environment variables.
Definition curl.ipp:217
void pause()
Pause publishing until a call to resume. Duplicate calls are allowed.
Definition curl.ipp:270
static const char * adc_curl_plugin_prog_default
default full path to curl utility This program should be world accessible. Overridden with env("ADC_C...
Definition curl.ipp:46
int initialize()
Ready the plugin to publish following the configuration options set or defaulted.
Definition curl.ipp:230
string_view name() const
Definition curl.ipp:278
string_view version() const
Definition curl.ipp:282
int config(const std::map< std::string, std::string > &m)
Configure the plugin with the options given.
Definition curl.ipp:211
const std::map< const std::string, const std::string > & get_option_defaults()
Look up the settable options and their defaults.
Definition curl.ipp:226
static const char * adc_curl_plugin_debug_default
ADC curl plugin enable debug messages; (default "0": none) "2" provides message send debugging "1" pr...
Definition curl.ipp:64
void resume()
Resume publishing Duplicate calls are allowed.
Definition curl.ipp:274
void finalize()
Stop publishing and release any resources held for managing publication.
Definition curl.ipp:258
int publish(std::shared_ptr< builder_api > b)
Publish the content of the builder.
Definition curl.ipp:174
static const char * adc_curl_plugin_directory_default
default scratch directory for curl utility messages. This directory must be globally writable and sho...
Definition curl.ipp:41
static const char * adc_curl_plugin_port_default
default ADC server https port. The server should be highly available. Overridden with env("ADC_CURL_P...
Definition curl.ipp:51
static const char * adc_curl_plugin_url_default
ADC data ingest server host. The server should be highly available; the default value of https://loca...
Definition curl.ipp:58
Publisher plugin interface.
Definition publisher.hpp:48
Definition adc.hpp:82
std::string_view string_view
Definition curl.ipp:18
std::string string
Definition curl.ipp:17