Update copyright year
[freeradius.git] / src / main / version.c
index 0febb88..2190444 100644 (file)
@@ -27,7 +27,8 @@ RCSID("$Id$")
 #include <freeradius-devel/radiusd.h>
 USES_APPLE_DEPRECATED_API      /* OpenSSL API has been deprecated by Apple */
 
-static uint64_t libmagic = RADIUSD_MAGIC_NUMBER;
+static uint64_t        libmagic = RADIUSD_MAGIC_NUMBER;
+char const     *radiusd_version_short = RADIUSD_VERSION_STRING;
 
 #ifdef HAVE_OPENSSL_CRYPTO_H
 #  include <openssl/crypto.h>
@@ -35,7 +36,12 @@ static uint64_t libmagic = RADIUSD_MAGIC_NUMBER;
 
 static long ssl_built = OPENSSL_VERSION_NUMBER;
 
-/** Check build and linked versions of OpenSSL match
+/** Check built and linked versions of OpenSSL match
+ *
+ * OpenSSL version number consists of:
+ * MNNFFPPS: major minor fix patch status
+ *
+ * Where status >= 0 && < 10 means beta, and status 10 means release.
  *
  * Startup check for whether the linked version of OpenSSL matches the
  * version the server was built against.
@@ -48,13 +54,31 @@ int ssl_check_consistency(void)
 
        ssl_linked = SSLeay();
 
-       if (ssl_linked != ssl_built) {
+       /*
+        *      Status mismatch always triggers error.
+        */
+       if ((ssl_linked & 0x0000000f) != (ssl_built & 0x0000000f)) {
+       mismatch:
                ERROR("libssl version mismatch.  built: %lx linked: %lx",
-                      (unsigned long) ssl_built,
-                      (unsigned long) ssl_linked);
+                     (unsigned long) ssl_built,
+                     (unsigned long) ssl_linked);
 
                return -1;
-       };
+       }
+
+       /*
+        *      Use the OpenSSH approach and relax fix checks after version
+        *      1.0.0 and only allow moving backwards within a patch
+        *      series.
+        */
+       if (ssl_built & 0xf0000000) {
+               if ((ssl_built & 0xfffff000) != (ssl_linked & 0xfffff000) ||
+                   (ssl_built & 0x00000ff0) > (ssl_linked & 0x00000ff0)) goto mismatch;
+       /*
+        *      Before 1.0.0 we require the same major minor and fix version
+        *      and ignore the patch number.
+        */
+       } else if ((ssl_built & 0xfffff000) != (ssl_linked & 0xfffff000)) goto mismatch;
 
        return 0;
 }
@@ -66,26 +90,52 @@ int ssl_check_consistency(void)
  * @param v version to convert.
  * @return pointer to a static buffer containing the version string.
  */
-char const *ssl_version_by_num(uint64_t v)
+char const *ssl_version_by_num(uint32_t v)
 {
-       /* 2 (%s) + 1 (.) + 2 (%i) + 1 (.) + 2 (%i) + 1 (c) + 1 (-) + 2 (%i) + \0 */
-       static char buffer[13];
+       /* 2 (%s) + 1 (.) + 2 (%i) + 1 (.) + 2 (%i) + 1 (c) + 8 (%s) + \0 */
+       static char buffer[18];
        char *p = buffer;
 
-       p += sprintf(p, "%i.%i.%i",
-                    (int) ((0xff0000000 & v) >> 28),
-                    (int) ((0x00ff00000 & v) >> 20),
-                    (int) ((0x0000ff000 & v) >> 12));
+       p += sprintf(p, "%u.%u.%u",
+                    (0xf0000000 & v) >> 28,
+                    (0x0ff00000 & v) >> 20,
+                    (0x000ff000 & v) >> 12);
 
-       if ((0x000000ff0 & v) >> 4) {
-               *p++ =  (char) (0x60 + ((0x000000ff0 & v) >> 4));
+       if ((0x00000ff0 & v) >> 4) {
+               *p++ =  (char) (0x60 + ((0x00000ff0 & v) >> 4));
        }
 
-       sprintf(p, "-%i", (int) (0x00000000f & v));
+       *p++ = ' ';
+
+       /*
+        *      Development (0)
+        */
+       if ((0x0000000f & v) == 0) {
+               strcpy(p, "dev");
+       /*
+        *      Beta (1-14)
+        */
+       } else if ((0x0000000f & v) <= 14) {
+               sprintf(p, "beta %u", 0x0000000f & v);
+       } else {
+               strcpy(p, "release");
+       }
 
        return buffer;
 }
 
+/** Return the linked SSL version number as a string
+ *
+ * @return pointer to a static buffer containing the version string.
+ */
+char const *ssl_version_num(void)
+{
+       long ssl_linked;
+
+       ssl_linked = SSLeay();
+       return ssl_version_by_num((uint32_t)ssl_linked);
+}
+
 /** Convert two openssl version numbers into a range string
  *
  * @note Not thread safe.
@@ -94,7 +144,7 @@ char const *ssl_version_by_num(uint64_t v)
  * @param high version to convert.
  * @return pointer to a static buffer containing the version range string.
  */
-char const *ssl_version_range(uint64_t low, uint64_t high)
+char const *ssl_version_range(uint32_t low, uint32_t high)
 {
        /* 12 (version) + 3 ( - ) + 12 (version) */
        static char buffer[28];
@@ -118,12 +168,12 @@ char const *ssl_version(void)
 {
        static char buffer[256];
 
-       uint64_t v = (uint64_t) SSLeay();
+       uint32_t v = SSLeay();
 
-       snprintf(buffer, sizeof(buffer), "%s 0x%.9" PRIx64 " (%s)",
+       snprintf(buffer, sizeof(buffer), "%s 0x%.8x (%s)",
                 SSLeay_version(SSLEAY_VERSION),                /* Not all builds include a useful version number */
                 v,
-                ssl_version_by_num((uint64_t) v));
+                ssl_version_by_num(v));
 
        return buffer;
 }
@@ -132,13 +182,17 @@ int ssl_check_consistency(void) {
        return 0;
 }
 
+char const *ssl_version_num(void)
+{
+       return "not linked";
+}
+
 char const *ssl_version()
 {
        return "not linked";
 }
 #endif /* ifdef HAVE_OPENSSL_CRYPTO_H */
 
-
 /** Check if the application linking to the library has the correct magic number
  *
  * @param magic number as defined by RADIUSD_MAGIC_NUMBER
@@ -170,91 +224,360 @@ int rad_check_lib_magic(uint64_t magic)
        return 0;
 }
 
-/*
- *     Display the revision number for this program
+/** Add a feature flag to the main configuration
+ *
+ * Add a feature flag (yes/no) to the 'feature' subsection
+ * off the main config.
+ *
+ * This allows the user to create configurations that work with
+ * across multiple environments.
+ *
+ * @param cs to add feature pair to.
+ * @param name of feature.
+ * @param enabled Whether the feature is present/enabled.
+ * @return 0 on success else -1.
+ */
+int version_add_feature(CONF_SECTION *cs, char const *name, bool enabled)
+{
+       if (!cs) return -1;
+
+       if (!cf_pair_find(cs, name)) {
+               CONF_PAIR *cp;
+
+               cp = cf_pair_alloc(cs, name, enabled ? "yes" : "no",
+                                  T_OP_SET, T_BARE_WORD, T_BARE_WORD);
+               if (!cp) return -1;
+               cf_pair_add(cs, cp);
+       }
+
+       return 0;
+}
+
+/** Add a library/server version pair to the main configuration
+ *
+ * Add a version number to the 'version' subsection off the main
+ * config.
+ *
+ * Because of the optimisations in the configuration parser, these
+ * may be checked using regular expressions without a performance
+ * penalty.
+ *
+ * The version pairs are there primarily to work around defects
+ * in libraries or the server.
+ *
+ * @param cs to add feature pair to.
+ * @param name of library or feature.
+ * @param version Humanly readable version text.
+ * @return 0 on success else -1.
  */
-void version(void)
+int version_add_number(CONF_SECTION *cs, char const *name, char const *version)
 {
-       INFO("%s: %s", progname, radiusd_version);
+       CONF_PAIR *old;
+
+       if (!cs) return -1;
+
+       old = cf_pair_find(cs, name);
+       if (!old) {
+               CONF_PAIR *cp;
 
-       DEBUG3("Server was built with: ");
+               cp = cf_pair_alloc(cs, name, version, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
+               if (!cp) return -1;
 
+               cf_pair_add(cs, cp);
+       } else {
+               WARN("Replacing user version.%s (%s) with %s", name, cf_pair_value(old), version);
+
+               cf_pair_replace(cs, old, version);
+       }
+
+       return 0;
+}
+
+
+/** Initialise core feature flags
+ *
+ * @param cs Where to add the CONF_PAIRS, if null pairs will be added
+ *     to the 'feature' section of the main config.
+ */
+void version_init_features(CONF_SECTION *cs)
+{
+       version_add_feature(cs, "accounting",
 #ifdef WITH_ACCOUNTING
-       DEBUG3("  accounting");
+                               true
+#else
+                               false
 #endif
-       DEBUG3("  authentication"); /* always enabled */
+                               );
+
+       version_add_feature(cs, "authentication", true);
 
+       version_add_feature(cs, "ascend-binary-attributes",
 #ifdef WITH_ASCEND_BINARY
-       DEBUG3("  ascend binary attributes");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "coa",
 #ifdef WITH_COA
-       DEBUG3("  coa");
+                               true
+#else
+                               false
 #endif
+                               );
+
+
+       version_add_feature(cs, "control-socket",
 #ifdef WITH_COMMAND_SOCKET
-       DEBUG3("  control-socket");
+                               true
+#else
+                               false
 #endif
+                               );
+
+
+       version_add_feature(cs, "detail",
 #ifdef WITH_DETAIL
-       DEBUG3("  detail");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "dhcp",
 #ifdef WITH_DHCP
-       DEBUG3("  dhcp");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "dynamic-clients",
 #ifdef WITH_DYNAMIC_CLIENTS
-       DEBUG3("  dynamic clients");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "osfc2",
 #ifdef OSFC2
-       DEBUG3("  OSFC2");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "proxy",
 #ifdef WITH_PROXY
-       DEBUG3("  proxy");
+                               true
+#else
+                               false
 #endif
-#ifdef HAVE_PCREPOSIX_H
-       DEBUG3("  regex-pcre");
+                               );
+
+       version_add_feature(cs, "regex-pcre",
+#ifdef HAVE_PCRE
+                               true
 #else
-#ifdef HAVE_REGEX_H
-       DEBUG3("  regex-posix");
+                               false
 #endif
+                               );
+
+#if !defined(HAVE_PCRE) && defined(HAVE_REGEX)
+       version_add_feature(cs, "regex-posix", true);
+       version_add_feature(cs, "regex-posix-extended",
+#  ifdef HAVE_REG_EXTENDED
+                               true
+#  else
+                               false
+#  endif
+                               );
+#else
+       version_add_feature(cs, "regex-posix", false);
+       version_add_feature(cs, "regex-posix-extended", false);
 #endif
 
+       version_add_feature(cs, "session-management",
 #ifdef WITH_SESSION_MGMT
-       DEBUG3("  session-management");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "stats",
 #ifdef WITH_STATS
-       DEBUG3("  stats");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "tcp",
 #ifdef WITH_TCP
-       DEBUG3("  tcp");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "threads",
 #ifdef WITH_THREADS
-       DEBUG3("  threads");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "tls",
 #ifdef WITH_TLS
-       DEBUG3("  tls");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "unlang",
 #ifdef WITH_UNLANG
-       DEBUG3("  unlang");
+                               true
+#else
+                               false
 #endif
+                               );
+
+       version_add_feature(cs, "vmps",
 #ifdef WITH_VMPS
-       DEBUG3("  vmps");
+                               true
+#else
+                               false
+#endif
+                               );
+
+       version_add_feature(cs, "developer",
+#ifndef NDEBUG
+                               true
+#else
+                               false
+#endif
+                               );
+}
+
+/** Initialise core version flags
+ *
+ * @param cs Where to add the CONF_PAIRS, if null pairs will be added
+ *     to the 'version' section of the main config.
+ */
+void version_init_numbers(CONF_SECTION *cs)
+{
+       char buffer[128];
+
+       version_add_number(cs, "freeradius-server", radiusd_version_short);
+
+       snprintf(buffer, sizeof(buffer), "%i.%i.*", talloc_version_major(), talloc_version_minor());
+       version_add_number(cs, "talloc", buffer);
+
+       version_add_number(cs, "ssl", ssl_version_num());
+
+#if defined(HAVE_REGEX) && defined(HAVE_PCRE)
+       version_add_number(cs, "pcre", pcre_version());
 #endif
+}
+
+static char const *spaces = "                                    ";    /* 40 */
+
+/*
+ *     Display the revision number for this program
+ */
+void version_print(void)
+{
+       CONF_SECTION *features, *versions;
+       CONF_ITEM *ci;
+       CONF_PAIR *cp;
+
+       if (DEBUG_ENABLED3) {
+               int max = 0, len;
+
+               MEM(features = cf_section_alloc(NULL, "feature", NULL));
+               version_init_features(features);
+
+               MEM(versions = cf_section_alloc(NULL, "version", NULL));
+               version_init_numbers(versions);
+
+               DEBUG2("Server was built with: ");
 
-       DEBUG3("Server core libs:");
-       DEBUG3("  talloc : %i.%i.*", talloc_version_major(), talloc_version_minor());
-       DEBUG3("  ssl    : %s", ssl_version());
+               for (ci = cf_item_find_next(features, NULL);
+                    ci;
+                    ci = cf_item_find_next(features, ci)) {
+                       len = talloc_array_length(cf_pair_attr(cf_item_to_pair(ci)));
+                       if (max < len) max = len;
+               }
 
-       DEBUG3("Library magic number:");
-       DEBUG3("  0x%llx", (unsigned long long) libmagic);
+               for (ci = cf_item_find_next(versions, NULL);
+                    ci;
+                    ci = cf_item_find_next(versions, ci)) {
+                       len = talloc_array_length(cf_pair_attr(cf_item_to_pair(ci)));
+                       if (max < len) max = len;
+               }
 
-       DEBUG3("Endianess:");
-#if defined(LITTLE_ENDIAN)
-       DEBUG3("  little");
-#elif defined(BIG_ENDIAN)
-       DEBUG3("  big");
+
+               for (ci = cf_item_find_next(features, NULL);
+                    ci;
+                    ci = cf_item_find_next(features, ci)) {
+                       char const *attr;
+
+                       cp = cf_item_to_pair(ci);
+                       attr = cf_pair_attr(cp);
+
+                       DEBUG2("  %s%.*s : %s", attr,
+                              (int)(max - talloc_array_length(attr)), spaces,  cf_pair_value(cp));
+               }
+
+               talloc_free(features);
+
+               DEBUG2("Server core libs:");
+
+               for (ci = cf_item_find_next(versions, NULL);
+                    ci;
+                    ci = cf_item_find_next(versions, ci)) {
+                       char const *attr;
+
+                       cp = cf_item_to_pair(ci);
+                       attr = cf_pair_attr(cp);
+
+                       DEBUG2("  %s%.*s : %s", attr,
+                              (int)(max - talloc_array_length(attr)), spaces,  cf_pair_value(cp));
+               }
+
+               talloc_free(versions);
+
+               DEBUG2("Endianness:");
+#if defined(FR_LITTLE_ENDIAN)
+               DEBUG2("  little");
+#elif defined(FR_BIG_ENDIAN)
+               DEBUG2("  big");
 #else
-       DEBUG3("  unknown");
+               DEBUG2("  unknown");
 #endif
 
-       INFO("Copyright (C) 1999-2014 The FreeRADIUS server project and contributors");
+               DEBUG2("Compilation flags:");
+#ifdef BUILT_WITH_CPPFLAGS
+               DEBUG2("  cppflags : " BUILT_WITH_CPPFLAGS);
+#endif
+#ifdef BUILT_WITH_CFLAGS
+               DEBUG2("  cflags   : " BUILT_WITH_CFLAGS);
+#endif
+#ifdef BUILT_WITH_LDFLAGS
+               DEBUG2("  ldflags  : " BUILT_WITH_LDFLAGS);
+#endif
+#ifdef BUILT_WITH_LIBS
+               DEBUG2("  libs     : " BUILT_WITH_LIBS);
+#endif
+               DEBUG2("  ");
+       }
+       INFO("FreeRADIUS Version " RADIUSD_VERSION_STRING);
+       INFO("Copyright (C) 1999-2017 The FreeRADIUS server project and contributors");
        INFO("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A");
        INFO("PARTICULAR PURPOSE");
        INFO("You may redistribute copies of FreeRADIUS under the terms of the");