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