diff options
-rw-r--r-- | libbutl/openssl.hxx | 35 | ||||
-rw-r--r-- | libbutl/openssl.ixx | 9 | ||||
-rw-r--r-- | libbutl/openssl.txx | 63 | ||||
-rw-r--r-- | tests/openssl/driver.cxx | 24 |
4 files changed, 127 insertions, 4 deletions
diff --git a/libbutl/openssl.hxx b/libbutl/openssl.hxx index 58e38f8..b340f5c 100644 --- a/libbutl/openssl.hxx +++ b/libbutl/openssl.hxx @@ -8,8 +8,10 @@ #include <libbutl/path.hxx> #include <libbutl/process.hxx> +#include <libbutl/optional.hxx> #include <libbutl/fdstream.hxx> #include <libbutl/small-vector.hxx> +#include <libbutl/semantic-version.hxx> #include <libbutl/export.hxx> @@ -78,6 +80,23 @@ namespace butl // department (that were apparently fixed in 1.0.2). To work around these // bugs pass user-supplied options first. // + struct openssl_info + { + // Note that the program name can be used by the caller to properly + // interpret the version. + // + // The name/version examples: + // + // OpenSSL 3.0.0 + // OpenSSL 1.1.1l + // LibreSSL 2.8.3 + // + // The `l` component above ends up in semantic_version::build. + // + std::string name; + semantic_version version; + }; + class LIBBUTL_SYMEXPORT openssl: public process { public: @@ -111,6 +130,22 @@ namespace butl const std::string& command, A&&... options); + // Run `openssl version` command and try to parse and return the + // information it prints to stdout. Return nullopt if the process hasn't + // terminated successfully or stdout parsing has failed. Throw + // process_error and io_error in case of errors. + // + template <typename E> + static optional<openssl_info> + info (E&& err, const process_env&); + + template <typename C, + typename E> + static optional<openssl_info> + info (const C&, + E&& err, + const process_env&); + private: template <typename T> struct is_other diff --git a/libbutl/openssl.ixx b/libbutl/openssl.ixx index 1435dcb..db2fbcd 100644 --- a/libbutl/openssl.ixx +++ b/libbutl/openssl.ixx @@ -26,4 +26,13 @@ namespace butl std::forward<A> (options)...) { } + + template <typename E> + inline optional<openssl_info> openssl:: + info (E&& err, const process_env& env) + { + return info ([] (const char* [], std::size_t) {}, + std::forward<E> (err), + env); + } } diff --git a/libbutl/openssl.txx b/libbutl/openssl.txx index f198c22..01e854c 100644 --- a/libbutl/openssl.txx +++ b/libbutl/openssl.txx @@ -1,6 +1,7 @@ // file : libbutl/openssl.txx -*- C++ -*- // license : MIT; see accompanying LICENSE file +#include <cstddef> // size_t #include <utility> // forward() namespace butl @@ -49,4 +50,66 @@ namespace butl // Note: leaving this scope closes any open ends of the pipes in io_data. } + + template <typename C, + typename E> + optional<openssl_info> openssl:: + info (const C& cmdc, E&& err, const process_env& env) + { + using namespace std; + + // Run the `openssl version` command. + // + openssl os (cmdc, + nullfd, fdstream_mode::text, forward<E> (err), + env, + "version"); + + // Read the command's stdout and wait for its completion. Bail out if the + // command didn't terminate successfully or stdout contains no data. + // + string s; + if (!getline (os.in, s)) + return nullopt; + + os.in.close (); + + if (!os.wait ()) + return nullopt; + + // Parse the version string. + // + // Note that there is some variety in the version representations: + // + // OpenSSL 3.0.0 7 sep 2021 (Library: OpenSSL 3.0.0 7 sep 2021) + // OpenSSL 1.1.1l FIPS 24 Aug 2021 + // LibreSSL 2.8.3 + // + // We will only consider the first two space separated components as the + // program name and version. We will also assume that there are no leading + // spaces and the version is delimited from the program name with a single + // space character. + // + size_t e (s.find (' ')); + + // Bail out if there is no version present in the string or the program + // name is empty. + // + if (e == string::npos || e == 0) + return nullopt; + + string nm (s, 0, e); + + size_t b (e + 1); // The beginning of the version. + e = s.find (' ', b); // The end of the version. + + optional<semantic_version> ver ( + parse_semantic_version (string (s, b, e != string::npos ? e - b : e), + "" /* build_separators */)); + + if (!ver) + return nullopt; + + return openssl_info {move (nm), move (*ver)}; + } } diff --git a/tests/openssl/driver.cxx b/tests/openssl/driver.cxx index d671c00..55f91dd 100644 --- a/tests/openssl/driver.cxx +++ b/tests/openssl/driver.cxx @@ -23,12 +23,28 @@ int main (int, const char* argv[]) try { - openssl os (nullfd, path ("-"), 2, path ("openssl"), "rand", 128); + using butl::optional; - vector<char> r (os.in.read_binary ()); - os.in.close (); + // Test openssl rand command. + // + { + openssl os (nullfd, path ("-"), 2, path ("openssl"), "rand", 128); - return os.wait () && r.size () == 128 ? 0 : 1; + vector<char> r (os.in.read_binary ()); + os.in.close (); + + assert (os.wait () && r.size () == 128); + } + + // Test openssl info retrieval. + // + { + optional<openssl_info> v (openssl::info (2, path ("openssl"))); + + assert (v); + } + + return 0; } catch (const system_error& e) { |