libadc-cxx 1.0.0
Structured logging for scientific computing
Loading...
Searching...
No Matches
script.ipp
Go to the documentation of this file.
3#include <cstdlib>
4#include <iostream>
5#include <fstream>
6#include <filesystem>
7#include <unistd.h>
8
9namespace adc {
10
11using std::cout;
12
13typedef std::string string;
14typedef std::string_view string_view;
15
16
17/*! \brief User script publisher plugin. The program specified
18 by environment variable is used to asynchronously deliver
19 published messages. It is invoked in a shell as:
20 ($prog $messagefile > /dev/null 2>&1 || /bin/rm $messagefile) &
21 */
23 enum state {
24 ok,
25 err
26 };
27 enum mode {
28 /* the next mode needed for correct operation */
29 pi_config,
30 pi_init,
31 pi_pub_or_final
32 };
33
34public:
35 /// \brief The default scratch directory for user script messages.
36 /// This directory must be globally writable and should be fast.
37 /// Overridden with env("ADC_SCRIPT_PLUGIN_DIRECTORY").
38 static const inline char *adc_script_plugin_dir_default = "/dev/shm/adc";
39
40 /// \brief The default path to a user script or program.
41 /// This program should be user accessible.
42 /// Overridden with env("ADC_SCRIPT_PLUGIN_PROG").
43 /// The default : is a non-operation.
44 static const inline char *adc_script_plugin_prog_default = ":";
45
46 /// \brief ADC script plugin enable debug messages; (default "0": none)
47 /// "2" provides message send debugging
48 /// "1" provides lighter debugging
49 /// Overridden with env("ADC_SCRIPT_PLUGIN_DEBUG").
50 inline static const char *adc_script_plugin_debug_default = "0";
51
52private:
53 const std::map< const string, const string > plugin_script_config_defaults =
54 { {"DIRECTORY", adc_script_plugin_dir_default},
57 };
58 inline static const char *plugin_prefix = "ADC_SCRIPT_PLUGIN_";
59 const string vers;
60 const std::vector<string> tags;
61 string fdir;
62 string prog;
63 int debug;
64 enum state state;
65 bool paused;
66 enum mode mode;
67
68 int config(const string& dir, string_view sprog, const string &sdebug) {
69 if (mode != pi_config)
70 return 2;
71 uid_t ui = geteuid();
72 fdir = dir + string("/") + std::to_string(ui);
73 prog = sprog;
74 mode = pi_init;
75 std::stringstream ss(sdebug);
76 ss >> debug;
77 if (debug < 0) {
78 debug = 0;
79 }
80 if (debug > 0) {
81 std::cout<< "script plugin configured" <<std::endl;
82 }
83 return 0;
84 }
85
86 // find field in m, then with prefix in env, then the default.
87 const string get(const std::map< string, string >& m,
88 string field, string_view env_prefix) {
89 // fields not defined in config_defaults raise an exception.
90 auto it = m.find(field);
91 if (it != m.end()) {
92 return it->second;
93 }
94 string en = string(env_prefix) += field;
95 char *ec = getenv(en.c_str());
96 if (!ec) {
97 return plugin_script_config_defaults.at(field);
98 } else {
99 return string(ec);
100 }
101 }
102
103 string get_temp_file_name() {
104 string tplt = fdir + "/json-sh-msg-XXXXXX";
105 char * cstr = new char [tplt.length()+1];
106 std::strcpy (cstr, tplt.c_str());
107 int fd = mkstemp(cstr);
108 if (fd >= 0) {
109 string f(cstr);
110 delete[] cstr;
111 close(fd);
112 // current user now owns tempfile.
113 return f;
114 }
115 std::cout<< "'script' plugin: mkstemp failed" <<std::endl;
116 delete[] cstr;
117 return "";
118 }
119
120 // invoke script with json in f as arg 1, then delete f.
121 // wrap this in a thread to make it non-blocking eventually.
122 int script_send(string& f)
123 {
124 string qcmd = "(" + prog + " " + f +
125 "> /dev/null 2>&1 || /bin/rm " + f + ") &";
126 int err2 = std::system(qcmd.c_str());
127 if (debug > 1) {
128 std::cout << "script plugin file: " << f << std::endl;
129 std::cout << "script plugin err: " << err2 << std::endl;
130 }
131 return 0;
132 }
133
134public:
135 script_plugin() : vers("1.0.0") , tags({"none"}), debug(0), state(ok), paused(false), mode(pi_config) { }
136
137 int publish(std::shared_ptr<builder_api> b) {
138 if (paused)
139 return 0;
140 if (state != ok)
141 return 1;
142 if (mode != pi_pub_or_final)
143 return 2;
144 // write to tmpfile, run script and then delete tmpfile
145 // this could be made fancy with a work queue in a later reimplementation.
146 // try for /dev/shm/adc for performance and fall back to tmpdir
147 string fname = get_temp_file_name();
148 if (debug) {
149 std::cout << "script plugin temp name: " << fname <<std::endl;
150 }
151 // open dump file
152 std::ofstream out(fname);
153 if (out.good()) {
154 out << b->serialize() << std::endl;
155 if (out.good()) {
156 if (debug) {
157 std::cout << "plugin 'script' wrote" << std::endl;
158 }
159 out.close();
160 script_send(fname); // send also removes file
161 return 0;
162 } else {
163 std::cout << "script plugin failed write" << std::endl;
164 return 1;
165 }
166 }
167 std::filesystem::remove(fname);
168 std::cout << "script plugin failed open " <<fname<< std::endl;
169 return 1;
170 }
171
172 int config(const std::map< std::string, std::string >& m) {
173 return config(m, plugin_prefix );
174 }
175
176 int config(const std::map< std::string, std::string >& m, std::string_view env_prefix) {
177 string d = get(m, "DIRECTORY", env_prefix);
178 string prog = get(m, "PROG", env_prefix);
179 string sdebug = get(m, "DEBUG", env_prefix);
180 return config(d, prog, sdebug);
181 }
182
183 const std::map< const std::string, const std::string> & get_option_defaults() {
184 return plugin_script_config_defaults;
185 }
186
188 std::map <string, string >m;
189 // config if never config'd
190 if (!fdir.size())
191 config(m);
192 if (mode != pi_init) {
193 return 2;
194 }
195 if ( state == err ) {
196 std::cout << "script plugin initialize found pre-existing error" << std::endl;
197 return 3;
198 }
199 std::error_code ec;
200 std::filesystem::create_directories(fdir, ec);
201 if (ec.value() != 0 && ec.value() != EEXIST ) {
202 state = err;
203 std::cout << "unable to create scratch directory for plugin 'script'; "
204 << fdir << " : " << ec.message() << std::endl;
205 return ec.value();
206 } else {
207 if (debug) {
208 std::cout << "created " << fdir <<std::endl;
209 }
210 mode = pi_pub_or_final;
211 }
212 return 0;
213 }
214
215 void finalize() {
216 if (mode == pi_pub_or_final) {
217 state = ok;
218 paused = false;
219 mode = pi_config;
220 } else {
221 if (debug) {
222 std::cout << "script plugin finalize on non-running plugin" << std::endl;
223 }
224 }
225 }
226
227 void pause() {
228 paused = true;
229 }
230
231 void resume() {
232 paused = false;
233 }
234
236 return "script";
237 }
238
240 return vers;
241 }
242
244 if (debug) {
245 std::cout << "Destructing script_plugin" << std::endl;
246 }
247 }
248};
249
250} // adc
Publisher plugin interface.
Definition publisher.hpp:44
User script publisher plugin. The program specified by environment variable is used to asynchronously...
Definition script.ipp:22
string_view version() const
Definition script.ipp:239
int config(const std::map< std::string, std::string > &m)
Configure the plugin with the options given.
Definition script.ipp:172
int publish(std::shared_ptr< builder_api > b)
Publish the content of the builder.
Definition script.ipp:137
const std::map< const std::string, const std::string > & get_option_defaults()
Look up the settable options and their defaults.
Definition script.ipp:183
static const char * adc_script_plugin_debug_default
ADC script plugin enable debug messages; (default "0": none) "2" provides message send debugging "1" ...
Definition script.ipp:50
void pause()
Pause publishing until a call to resume. Duplicate calls are allowed.
Definition script.ipp:227
static const char * adc_script_plugin_prog_default
The default path to a user script or program. This program should be user accessible....
Definition script.ipp:44
string_view name() const
Definition script.ipp:235
static const char * adc_script_plugin_dir_default
The default scratch directory for user script messages. This directory must be globally writable and ...
Definition script.ipp:38
void finalize()
Stop publishing and release any resources held for managing publication.
Definition script.ipp:215
int config(const std::map< std::string, std::string > &m, std::string_view env_prefix)
Configure the plugin with the options given and the corresponding environment variables.
Definition script.ipp:176
int initialize()
Ready the plugin to publish following the configuration options set or defaulted.
Definition script.ipp:187
void resume()
Resume publishing Duplicate calls are allowed.
Definition script.ipp:231
Definition adc.hpp:75
std::string_view string_view
Definition curl.ipp:14
std::string string
Definition curl.ipp:13