dodo  0.0.1
A C++ library to create containerized Linux services
util.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the dodo library (https://github.com/jmspit/dodo).
3  * Copyright (c) 2019 Jan-Marten Spit.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19  * @file util.cpp
20  * Implement utility things.
21  */
22 
23 #include <common/exception.hpp>
24 #include <common/util.hpp>
25 
26 #include <fstream>
27 #include <iomanip>
28 #include <iostream>
29 #include <ios>
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/statvfs.h>
34 #include <unistd.h>
35 
36 
37 namespace dodo::common {
38 
39  bool fileReadInt( const std::string &file, int &i ) {
40  std::ifstream f(file);
41  f >> i;
42  return f.good();
43  }
44 
45  /**
46  * Return ASCII representation of char.
47  * @param c The char.
48  * @return the ASCII representation.
49  */
50  std::string strASCII( char c ) {
51  if ( c < 32 || c == 127 ) {
52  switch ( c ) {
53  case 0: return "NUL";
54  case 1: return "SOH";
55  case 2: return "STX";
56  case 3: return "ETX";
57  case 4: return "EOT";
58  case 5: return "ENQ";
59  case 6: return "ACK";
60  case 7: return "BEL";
61  case 8: return " BS";
62  case 9: return " HT";
63  case 10: return " LF";
64  case 11: return " VT";
65  case 12: return " FF";
66  case 13: return " CR";
67  case 14: return " SO";
68  case 15: return " SI";
69  case 16: return "DLE";
70  case 17: return "DC1";
71  case 18: return "DC2";
72  case 19: return "DC3";
73  case 20: return "DC4";
74  case 21: return "NAK";
75  case 22: return "SYN";
76  case 23: return "ETB";
77  case 24: return "CAN";
78  case 25: return " EM";
79  case 26: return "SUB";
80  case 27: return "ESC";
81  case 28: return " FS";
82  case 29: return " GS";
83  case 30: return " RS";
84  case 31: return " US";
85  case 127: return "DEL";
86  default: return "???";
87  }
88  } else if ( c < 255 ) {
89  std::stringstream ss;
90  ss << std::setfill(' ') << std::setw(3) << c;
91  return ss.str();
92  } else {
93  std::stringstream ss;
94  ss << std::setw(3) << std::setfill('0') << std::hex << std::setw(3) << c;
95  return ss.str();
96  }
97  }
98 
99  /**
100  * Dump binary data to a std::ostream.
101  * @param out The std::stream to dump to.
102  * @param s The char stream.
103  * @param width The max character width of the output.
104  */
105  void dumpBinaryData( std::ostream &out, const std::string &s, size_t width ) {
106  std::ios_base::fmtflags orgflags = out.flags();
107  size_t idx = 0;
108  size_t line_idx = 0;
109  std::stringstream binary_line;
110  std::stringstream char_line;
111  while ( idx < s.size() ) {
112  if ( line_idx >= width ) {
113  out << std::setfill('0') << std::setw(6) << idx-width << binary_line.str() << std::endl;
114  out << std::string(6,' ') << char_line.str() << std::endl;
115  binary_line.str( "" );
116  char_line.str( "" );
117  line_idx = 0;
118  }
119  unsigned int v = (unsigned char)s[idx];
120  binary_line << ' ' << std::setw(2) << std::setfill('0') << std::hex << v << ' ';
121  char_line << strASCII( s[idx] ) << ' ';
122  idx++;
123  line_idx++;
124  }
125  if ( line_idx ) {
126  out << std::setfill('0') << std::setw(6) << idx-width << binary_line.str() << std::endl;
127  out << std::string(6,' ') << char_line.str();
128  }
129  out.flags( orgflags );
130  }
131 
132  std::string bio2String( BIO* bio ) {
133  char *data = NULL;
134  long length = BIO_get_mem_data( bio, &data );
135  BIO_get_mem_data( bio, &data );
136  return std::string( data, length );
137  }
138 
139 
140  size_t writeSSLErrors( std::ostream& out, char terminator ) {
141  size_t count = 0;
142  unsigned long error = 0;
143  // https://www.openssl.org/docs/man1.0.2/man3/ERR_error_string.html buf must be at least 120 bytes
144  char errbuf[200];
145  while ( ( error = ERR_get_error() ) ) {
146  ERR_error_string_n( error, errbuf, sizeof(errbuf) );
147  out << errbuf;
148  if ( terminator ) out << terminator;
149  count++;
150  }
151  return count;
152  }
153 
154  std::string getSSLErrors( char terminator ) {
155  std::stringstream ss;
156  if ( writeSSLErrors( ss, terminator ) ) {
157  return ss.str();
158  } else return "";
159  }
160 
161  std::string fileReadString( const std::string &filename ) {
162  std::ifstream ifs( filename.c_str() );
163  std::string line = "";
164  if ( ifs.good() ) {
165  getline( ifs, line );
166  } else throw_Exception( "failed to open '" << filename << "'" );
167  return line;
168  }
169 
170  std::vector<std::string> fileReadStrings( const std::string &filename ) {
171  std::ifstream ifs( filename.c_str() );
172  std::vector<std::string> tmp;
173  std::string line = "";
174  getline( ifs, line );
175  if ( ifs.good() ) throw_Exception( "failed to open '" << filename << "'" );
176  while ( ifs.good() ) {
177  tmp.push_back( line );
178  getline( ifs, line );
179  }
180  return tmp;
181  }
182 
183  std::vector<std::string> fileReadStrings( const std::string &filename, const std::regex& exp ) {
184  std::ifstream ifs( filename.c_str() );
185  std::vector<std::string> tmp;
186  std::string line = "";
187  getline( ifs, line );
188  if ( !ifs.good() ) throw_Exception( "failed to open '" << filename << "'" );
189  while ( ifs.good() ) {
190  std::smatch m;
191  if ( std::regex_match(line, m, exp ) ) tmp.push_back( line );
192  getline( ifs, line );
193  }
194  return tmp;
195  }
196 
197  std::string escapeJSON( const std::string &s ) {
198  std::ostringstream o;
199  for ( auto c = s.cbegin(); c != s.cend(); c++ ) {
200  switch (*c) {
201  case '"': o << "\\\""; break;
202  case '\\': o << "\\\\"; break;
203  case '\b': o << "\\b"; break;
204  case '\f': o << "\\f"; break;
205  case '\n': o << "\\n"; break;
206  case '\r': o << "\\r"; break;
207  case '\t': o << "\\t"; break;
208  default:
209  if ('\x00' <= *c && *c <= '\x1f') {
210  o << "\\u"
211  << std::hex << std::setw(4) << std::setfill('0') << (int)*c;
212  } else {
213  o << *c;
214  }
215  }
216  }
217  return o.str();
218 }
219 
220  bool fileReadAccess( const std::string& path ) {
221  bool result = true;
222  struct stat buf;
223  int r = stat( path.c_str(), &buf );
224  if ( r != 0 ) return false;
225  result = S_ISREG(buf.st_mode);
226  result = result && ( !access( path.c_str(), F_OK | R_OK ) );
227  return result;
228  }
229 
230  bool directoryExists( const std::string &path ) {
231  bool result = true;
232  struct stat buf;
233  int r = stat( path.c_str(), &buf );
234  if ( r != 0 ) return false;
235  result = S_ISDIR(buf.st_mode);
236  return result;
237  }
238 
239  bool directoryWritable( const std::string &path ) {
240  bool result = true;
241  struct stat buf;
242  int r = stat( path.c_str(), &buf );
243  if ( r != 0 ) return false;
244  result = result && S_ISDIR(buf.st_mode);
245  result = result && (S_IWUSR & buf.st_mode);
246  return result;
247  }
248 
249  bool availableFileSpace( const std::string &path, size_t &avail ) {
250  struct statvfs stat;
251  if ( statvfs( path.c_str(), &stat) != 0 ) return false;
252  avail = stat.f_bsize * stat.f_bavail;
253  return true;
254  }
255 
256  size_t getFileSize( const std::string &filename ) {
257  struct stat stat_buf;
258  int rc = stat( filename.c_str(), &stat_buf );
259  return rc == 0 ? stat_buf.st_size : 0;
260  }
261 
262  std::string formatDateTimeUTC( const struct timeval &tv ) {
263  struct tm utc;
264  gmtime_r( &tv.tv_sec, &utc );
265  std::stringstream ss;
266  ss << std::setfill('0') << std::setw(4) << utc.tm_year + 1900 << "-";
267  ss << std::setfill('0') << std::setw(2) << utc.tm_mon+1 << "-";
268  ss << std::setfill('0') << std::setw(2) << utc.tm_mday << "T";
269  ss << std::setfill('0') << std::setw(2) << utc.tm_hour << ":";
270  ss << std::setfill('0') << std::setw(2) << utc.tm_min << ":";
271  ss << std::setfill('0') << std::setw(2) << utc.tm_sec << ".";
272  ss << std::setfill('0') << std::setw(6) << tv.tv_usec << "Z";
273  return ss.str();
274  }
275 
276  template <class T> T YAML_read_key( const YAML::Node &node, const std::string& key ) {
277  if ( node[key] ) {
278  return node[key].as<T>();
279  } else throw_Exception( key << " parameter missing in YAML::Node" );
280  }
281 
282 
283  /**
284  * Instantiate template YAML_read_key for int
285  * @return the value as an int
286  */
287  template int YAML_read_key<int>( const YAML::Node &, const std::string& );
288 
289  /**
290  * Instantiate template YAML_read_key for size_t
291  * @return the value as a size_t
292  */
293  template size_t YAML_read_key<size_t>( const YAML::Node &, const std::string& );
294 
295  /**
296  * Instantiate template YAML_read_key for uint16_t
297  * @return the value as a uint16_t
298  */
299  template uint16_t YAML_read_key<uint16_t>( const YAML::Node &, const std::string& );
300 
301  /**
302  * Instantiate template YAML_read_key for unsigned int
303  * @return the value as an unsigned int
304  */
305  template unsigned int YAML_read_key<unsigned int>( const YAML::Node &, const std::string& );
306 
307  /**
308  * Instantiate template YAML_read_key for long
309  * @return the value as a long
310  */
311  template long YAML_read_key<long>( const YAML::Node &, const std::string& );
312 
313  /**
314  * Instantiate template YAML_read_key for double
315  * @return the value as a double
316  */
317  template double YAML_read_key<double>( const YAML::Node &, const std::string& );
318 
319  /**
320  * Instantiate template YAML_read_key for std::string
321  * @return the value as a std::string
322  */
323  template std::string YAML_read_key<std::string>( const YAML::Node &, const std::string& );
324 
325  /**
326  * Instantiate template YAML_read_key for bool
327  * @return the value as a bool
328  */
329  template bool YAML_read_key<bool>( const YAML::Node &, const std::string& );
330 
331  template <typename T> T YAML_read_key_default( const YAML::Node &node,
332  const std::string& key,
333  const T& default_value ) {
334  if ( node[key] ) {
335  return node[key].as<T>();
336  } else return default_value;
337  }
338 
339  /**
340  * Instantiate template YAML_read_key for int
341  * @return the value as an int
342  */
343  template int YAML_read_key_default<int>( const YAML::Node &, const std::string&, const int& );
344 
345  /**
346  * Instantiate template YAML_read_key for unsigned int
347  * @return the value as an unsigned int
348  */
349  template unsigned int YAML_read_key_default<unsigned int>( const YAML::Node &, const std::string&, const unsigned int& );
350 
351  /**
352  * Instantiate template YAML_read_key for size_t
353  * @return the value as a size_t
354  */
355  template size_t YAML_read_key_default<size_t>( const YAML::Node &, const std::string&, const size_t& );
356 
357  /**
358  * Instantiate template YAML_read_key for long
359  * @return the value as a long
360  */
361  template long YAML_read_key_default<long>( const YAML::Node &, const std::string&, const long& );
362 
363  /**
364  * Instantiate template YAML_read_key for double
365  * @return the value as a double
366  */
367  template double YAML_read_key_default<double>( const YAML::Node &, const std::string&, const double& );
368 
369  /**
370  * Instantiate template YAML_read_key for std::string
371  * @return the value as a std::string
372  */
373  template std::string YAML_read_key_default<std::string>( const YAML::Node &, const std::string&, const std::string& );
374 
375  /**
376  * Instantiate template YAML_read_key for bool
377  * @return the value as a bool
378  */
379  template bool YAML_read_key_default<bool>( const YAML::Node &, const std::string&, const bool& );
380 
381 }
dodo::common::YAML_read_key_default< long >
template long YAML_read_key_default< long >(const YAML::Node &, const std::string &, const long &)
Instantiate template YAML_read_key for long.
dodo::common::escapeJSON
std::string escapeJSON(const std::string &s)
Escape a JSOn string.
Definition: util.cpp:197
dodo::common::YAML_read_key< double >
template double YAML_read_key< double >(const YAML::Node &, const std::string &)
Instantiate template YAML_read_key for double.
dodo::common::YAML_read_key< unsigned int >
template unsigned int YAML_read_key< unsigned int >(const YAML::Node &, const std::string &)
Instantiate template YAML_read_key for unsigned int.
dodo::common::fileReadInt
bool fileReadInt(const std::string &file, int &i)
Read from a file, expecting it to contain a (signed) int.
Definition: util.cpp:39
dodo::common::fileReadString
std::string fileReadString(const std::string &filename)
Read the file as a single string.
Definition: util.cpp:161
dodo::common::YAML_read_key< uint16_t >
template uint16_t YAML_read_key< uint16_t >(const YAML::Node &, const std::string &)
Instantiate template YAML_read_key for uint16_t.
dodo::common::directoryExists
bool directoryExists(const std::string &path)
Return true when the directory exists.
Definition: util.cpp:230
dodo::common::YAML_read_key< bool >
template bool YAML_read_key< bool >(const YAML::Node &, const std::string &)
Instantiate template YAML_read_key for bool.
dodo::common::YAML_read_key_default< size_t >
template size_t YAML_read_key_default< size_t >(const YAML::Node &, const std::string &, const size_t &)
Instantiate template YAML_read_key for size_t.
dodo::common::writeSSLErrors
size_t writeSSLErrors(std::ostream &out, char terminator)
Write OpenSSL errors occurred in this thread to ostream, and clear their error state.
Definition: util.cpp:140
dodo::common::YAML_read_key_default< double >
template double YAML_read_key_default< double >(const YAML::Node &, const std::string &, const double &)
Instantiate template YAML_read_key for double.
dodo::common::strASCII
std::string strASCII(char c)
Return ASCII representation of char.
Definition: util.cpp:50
dodo::common::YAML_read_key_default< unsigned int >
template unsigned int YAML_read_key_default< unsigned int >(const YAML::Node &, const std::string &, const unsigned int &)
Instantiate template YAML_read_key for unsigned int.
dodo::common::dumpBinaryData
void dumpBinaryData(std::ostream &out, const std::string &s, size_t width)
Dump binary data to a std::ostream.
Definition: util.cpp:105
throw_Exception
#define throw_Exception(what)
Throws an Exception, passes FILE and LINE to constructor.
Definition: exception.hpp:174
dodo::common::availableFileSpace
bool availableFileSpace(const std::string &path, size_t &avail)
Return true when the free space could be determined, and set in avail.
Definition: util.cpp:249
dodo::common::bio2String
std::string bio2String(BIO *bio)
Convert the data contents of an OpenSSL BIO to a std::string.
Definition: util.cpp:132
dodo::common::YAML_read_key_default
T YAML_read_key_default(const YAML::Node &node, const std::string &key, const T &default_value)
Template function to check existence and read YAML values of arbitrary type.
Definition: util.cpp:331
dodo::common::getFileSize
size_t getFileSize(const std::string &path)
Return the size of the file.
Definition: util.cpp:256
dodo::common::getSSLErrors
std::string getSSLErrors(char terminator)
Get all OpenSSL errors as a single string, and clear their error state.
Definition: util.cpp:154
dodo::common
Common and utility interfaces.
Definition: application.hpp:29
dodo::common::YAML_read_key
T YAML_read_key(const YAML::Node &node, const std::string &key)
Template function to check existence and read YAML values of arbitrary type.
Definition: util.cpp:276
exception.hpp
dodo::common::YAML_read_key< long >
template long YAML_read_key< long >(const YAML::Node &, const std::string &)
Instantiate template YAML_read_key for long.
dodo::common::fileReadStrings
std::vector< std::string > fileReadStrings(const std::string &filename)
Read the file as vector of strings.
Definition: util.cpp:170
dodo::common::YAML_read_key< int >
template int YAML_read_key< int >(const YAML::Node &, const std::string &)
Instantiate template YAML_read_key for int.
dodo::common::formatDateTimeUTC
std::string formatDateTimeUTC(const struct timeval &tv)
Return a datetime string in UTC (2020-07-01T20:14:36.442929Z)
Definition: util.cpp:262
dodo::common::fileReadAccess
bool fileReadAccess(const std::string &path)
Return true when the file exists and the calling user has read access.
Definition: util.cpp:220
dodo::common::YAML_read_key_default< bool >
template bool YAML_read_key_default< bool >(const YAML::Node &, const std::string &, const bool &)
Instantiate template YAML_read_key for bool.
dodo::common::YAML_read_key< size_t >
template size_t YAML_read_key< size_t >(const YAML::Node &, const std::string &)
Instantiate template YAML_read_key for size_t.
dodo::common::directoryWritable
bool directoryWritable(const std::string &path)
Return true when the directory exists and is writable to the caller.
Definition: util.cpp:239
util.hpp
dodo::common::YAML_read_key_default< int >
template int YAML_read_key_default< int >(const YAML::Node &, const std::string &, const int &)
Instantiate template YAML_read_key for int.