"Fossies" - the Fresh Open Source Software Archive

Member "cfengine-3.15.4/libenv/sysinfo.c" (7 Jun 2021, 113575 Bytes) of package /linux/misc/cfengine-3.15.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2   Copyright 2019 Northern.tech AS
    3 
    4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
    5 
    6   This program is free software; you can redistribute it and/or modify it
    7   under the terms of the GNU General Public License as published by the
    8   Free Software Foundation; version 3.
    9 
   10   This program is distributed in the hope that it will be useful,
   11   but WITHOUT ANY WARRANTY; without even the implied warranty of
   12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13   GNU General Public License for more details.
   14 
   15   You should have received a copy of the GNU General Public License
   16   along with this program; if not, write to the Free Software
   17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
   18 
   19   To the extent this program is licensed as part of the Enterprise
   20   versions of CFEngine, the applicable Commercial Open Source License
   21   (COSL) may apply to this file if you as a licensee so wish it. See
   22   included file COSL.txt.
   23 */
   24 
   25 #include <platform.h>
   26 
   27 #include <sysinfo.h>
   28 #include <sysinfo_priv.h>
   29 #include <cf3.extern.h>
   30 #include <eval_context.h>
   31 #include <files_names.h>
   32 #include <files_interfaces.h>
   33 #include <hash.h>
   34 #include <scope.h>
   35 #include <item_lib.h>
   36 #include <matching.h>
   37 #include <systype.h>
   38 #include <unix.h>
   39 #include <string_lib.h>
   40 #include <regex.h>                                       /* StringMatchFull */
   41 #include <misc_lib.h>
   42 #include <file_lib.h>
   43 #include <rlist.h>
   44 #include <audit.h>
   45 #include <pipes.h>
   46 #include <known_dirs.h>
   47 #include <files_lib.h>
   48 #include <printsize.h>
   49 #include <cf-windows-functions.h>
   50 #include <ornaments.h>
   51 #include <feature.h>
   52 #include <evalfunction.h>
   53 #include <json-utils.h>
   54 
   55 #ifdef HAVE_ZONE_H
   56 # include <zone.h>
   57 #endif
   58 
   59 // HP-UX mpctl() for $(sys.cpus) on HP-UX - Mantis #1069
   60 #ifdef HAVE_SYS_MPCTL_H
   61 # include <sys/mpctl.h>
   62 #endif
   63 
   64 /* Linux.
   65    WARNING keep this before the #include <sys/sysctl.h> because of glibc bug:
   66    https://sourceware.org/bugzilla/show_bug.cgi?id=140 */
   67 #ifdef HAVE_STRUCT_SYSINFO_UPTIME
   68 # include <sys/sysinfo.h>
   69 #endif
   70 
   71 /* BSD, MAC OS X uptime calculation use KERN_BOOTTIME sysctl. */
   72 #ifdef HAVE_SYS_SYSCTL_H
   73 # ifdef HAVE_SYS_PARAM_H
   74 #  include <sys/param.h>
   75 # endif
   76 # include <sys/sysctl.h>
   77 #endif
   78 
   79 
   80 /*****************************************************/
   81 // Uptime calculation settings for GetUptimeSeconds() - Mantis #1134
   82 
   83 /* Listed here in priority order, i.e. first come the platform-specific
   84  * ways. If nothing works, one of the last, most generic ways should. */
   85 
   86 #ifndef __MINGW32__                 /* Windows is implemented in Enterprise */
   87 
   88 // HP-UX: pstat_getproc(2) on init (pid 1)
   89 #if defined(__hpux)
   90 # include <sys/param.h>
   91 # include <sys/pstat.h>
   92 # define BOOT_TIME_WITH_PSTAT_GETPROC
   93 
   94 // Solaris: kstat() for kernel statistics
   95 // See http://dsc.sun.com/solaris/articles/kstatc.html
   96 // BSD also has a kstat.h (albeit in sys), so check __sun just to be paranoid
   97 
   98 /**
   99  * @WARNING: Commented out because inside a Solaris 10 zone this gives the
  100  *           uptime of the host machine (the hypervisor). We thus choose to
  101  *           use UTMP for Solaris.
  102  */
  103 /*
  104 #elif defined(__sun) && defined(HAVE_KSTAT_H)
  105 # include <kstat.h>
  106 # define BOOT_TIME_WITH_KSTAT
  107 */
  108 
  109 // BSD: sysctl(3) to get kern.boottime, CPU count, etc.
  110 // See http://www.unix.com/man-page/FreeBSD/3/sysctl/
  111 // Linux also has sys/sysctl.h, so we check KERN_BOOTTIME to make sure it's BSD
  112 #elif defined(HAVE_SYS_SYSCTL_H) && defined(KERN_BOOTTIME)
  113 # define BOOT_TIME_WITH_SYSCTL
  114 
  115 // GNU/Linux: struct sysinfo.uptime
  116 #elif defined(HAVE_STRUCT_SYSINFO_UPTIME)
  117 # define BOOT_TIME_WITH_SYSINFO
  118 
  119 /* Generic System V way, available in most platforms. */
  120 #elif defined(HAVE_UTMP_H)
  121 # include <utmp.h>
  122 # define BOOT_TIME_WITH_UTMP
  123 
  124 /* POSIX alternative (utmp.h does not exist on BSDs). */
  125 #elif defined(HAVE_UTMPX_H)
  126 # include <utmpx.h>
  127 # define BOOT_TIME_WITH_UTMPX
  128 
  129 #else
  130 // Most generic way: {stat("/proc/1")}.st_ctime
  131 // TODO in Solaris zones init is not guaranteed to be PID 1!
  132 #define BOOT_TIME_WITH_PROCFS
  133 
  134 #endif
  135 
  136 /* Fallback uptime calculation: Parse the "uptime" command in case the
  137  * platform-specific way fails or returns absurd number. */
  138 static time_t GetBootTimeFromUptimeCommand(time_t now);
  139 
  140 #endif  /* ifndef __MINGW32__ */
  141 
  142 #define LSB_RELEASE_FILENAME "/etc/lsb-release"
  143 #define DEBIAN_VERSION_FILENAME "/etc/debian_version"
  144 #define DEBIAN_ISSUE_FILENAME "/etc/issue"
  145 
  146 
  147 /*****************************************************/
  148 
  149 void CalculateDomainName(const char *nodename, const char *dnsname,
  150                          char *fqname, size_t fqname_size,
  151                          char *uqname, size_t uqname_size,
  152                          char *domain, size_t domain_size);
  153 
  154 #ifdef __linux__
  155 static int Linux_Fedora_Version(EvalContext *ctx);
  156 static int Linux_Redhat_Version(EvalContext *ctx);
  157 static void Linux_Amazon_Version(EvalContext *ctx);
  158 static void Linux_Alpine_Version(EvalContext *ctx);
  159 static void Linux_Oracle_VM_Server_Version(EvalContext *ctx);
  160 static void Linux_Oracle_Version(EvalContext *ctx);
  161 static int Linux_Suse_Version(EvalContext *ctx);
  162 static int Linux_Slackware_Version(EvalContext *ctx, char *filename);
  163 static int Linux_Debian_Version(EvalContext *ctx);
  164 static int Linux_Misc_Version(EvalContext *ctx);
  165 static int Linux_Mandrake_Version(EvalContext *ctx);
  166 static int Linux_Mandriva_Version(EvalContext *ctx);
  167 static int Linux_Mandriva_Version_Real(EvalContext *ctx, char *filename, char *relstring, char *vendor);
  168 static int VM_Version(EvalContext *ctx);
  169 static int Xen_Domain(EvalContext *ctx);
  170 static int EOS_Version(EvalContext *ctx);
  171 static int MiscOS(EvalContext *ctx);
  172 static void OpenVZ_Detect(EvalContext *ctx);
  173 
  174 static bool ReadLine(const char *filename, char *buf, int bufsize);
  175 static FILE *ReadFirstLine(const char *filename, char *buf, int bufsize);
  176 #endif
  177 
  178 #ifdef XEN_CPUID_SUPPORT
  179 static void Xen_Cpuid(uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
  180 static bool Xen_Hv_Check(void);
  181 #endif
  182 
  183 static void GetCPUInfo(EvalContext *ctx);
  184 
  185 static const char *const CLASSATTRIBUTES[][3] =
  186 {
  187     [PLATFORM_CONTEXT_UNKNOWN] = {"-", "-", "-"},       /* as appear here are matched. The fields are sysname and machine */
  188     [PLATFORM_CONTEXT_OPENVZ] = {"virt_host_vz_vzps", ".*", ".*"}, /* VZ with vzps */
  189     [PLATFORM_CONTEXT_HP] = {"hp-ux", ".*", ".*"},      /* hpux */
  190     [PLATFORM_CONTEXT_AIX] = {"aix", ".*", ".*"},       /* aix */
  191     [PLATFORM_CONTEXT_LINUX] = {"linux", ".*", ".*"},   /* linux */
  192     [PLATFORM_CONTEXT_BUSYBOX] = {"busybox", ".*", ".*"}, /* linux w/ busybox - warning uname returns linux */
  193     [PLATFORM_CONTEXT_SOLARIS] = {"sunos", ".*",
  194                                   "5\\.1[1-9].*"},      /* new solaris, SunOS >= 5.11 */
  195     [PLATFORM_CONTEXT_SUN_SOLARIS] = {"sunos", ".*",
  196                                       "5\\.([2-9]|10)(\\..*)?"}, /* old solaris, SunOS < 5.11 */
  197     [PLATFORM_CONTEXT_FREEBSD] = {"freebsd", ".*", ".*"}, /* freebsd */
  198     [PLATFORM_CONTEXT_NETBSD] = {"netbsd", ".*", ".*"},   /* NetBSD */
  199     [PLATFORM_CONTEXT_CRAYOS] = {"sn.*", "cray*", ".*"},  /* cray */
  200     [PLATFORM_CONTEXT_WINDOWS_NT] = {"cygwin_nt.*", ".*", ".*"}, /* NT (cygwin) */
  201     [PLATFORM_CONTEXT_SYSTEMV] = {"unix_sv", ".*", ".*"}, /* Unixware */
  202     [PLATFORM_CONTEXT_OPENBSD] = {"openbsd", ".*", ".*"}, /* OpenBSD */
  203     [PLATFORM_CONTEXT_CFSCO] = {"sco_sv", ".*", ".*"},    /* SCO */
  204     [PLATFORM_CONTEXT_DARWIN] = {"darwin", ".*", ".*"},   /* Darwin, aka MacOS X */
  205     [PLATFORM_CONTEXT_QNX] = {"qnx", ".*", ".*"},         /* qnx  */
  206     [PLATFORM_CONTEXT_DRAGONFLY] = {"dragonfly", ".*", ".*"}, /* dragonfly */
  207     [PLATFORM_CONTEXT_MINGW] = {"windows_nt.*", ".*", ".*"},  /* NT (native) */
  208     [PLATFORM_CONTEXT_VMWARE] = {"vmkernel", ".*", ".*"}, /* VMWARE / ESX */
  209     [PLATFORM_CONTEXT_ANDROID] = {"android", ".*", ".*"}, /* android: Warning uname returns linux */
  210 };
  211 
  212 static const char *const VRESOLVCONF[] =
  213 {
  214     [PLATFORM_CONTEXT_UNKNOWN] = "-",
  215     [PLATFORM_CONTEXT_OPENVZ] = "/etc/resolv.conf",      /* virt_host_vz_vzps */
  216     [PLATFORM_CONTEXT_HP] = "/etc/resolv.conf",          /* hpux */
  217     [PLATFORM_CONTEXT_AIX] = "/etc/resolv.conf",         /* aix */
  218     [PLATFORM_CONTEXT_LINUX] = "/etc/resolv.conf",       /* linux */
  219     [PLATFORM_CONTEXT_BUSYBOX] = "/etc/resolv.conf",     /* linux */
  220     [PLATFORM_CONTEXT_SOLARIS] = "/etc/resolv.conf",     /* new solaris */
  221     [PLATFORM_CONTEXT_SUN_SOLARIS] = "/etc/resolv.conf", /* old solaris */
  222     [PLATFORM_CONTEXT_FREEBSD] = "/etc/resolv.conf",     /* freebsd */
  223     [PLATFORM_CONTEXT_NETBSD] = "/etc/resolv.conf",      /* netbsd */
  224     [PLATFORM_CONTEXT_CRAYOS] = "/etc/resolv.conf",      /* cray */
  225     [PLATFORM_CONTEXT_WINDOWS_NT] = "/etc/resolv.conf",  /* NT */
  226     [PLATFORM_CONTEXT_SYSTEMV] = "/etc/resolv.conf",     /* Unixware */
  227     [PLATFORM_CONTEXT_OPENBSD] = "/etc/resolv.conf",     /* openbsd */
  228     [PLATFORM_CONTEXT_CFSCO] = "/etc/resolv.conf",       /* sco */
  229     [PLATFORM_CONTEXT_DARWIN] = "/etc/resolv.conf",      /* darwin */
  230     [PLATFORM_CONTEXT_QNX] = "/etc/resolv.conf",         /* qnx */
  231     [PLATFORM_CONTEXT_DRAGONFLY] = "/etc/resolv.conf",   /* dragonfly */
  232     [PLATFORM_CONTEXT_MINGW] = "",                       /* mingw */
  233     [PLATFORM_CONTEXT_VMWARE] = "/etc/resolv.conf",      /* vmware */
  234     [PLATFORM_CONTEXT_ANDROID] = "",                     /* android */
  235 };
  236 
  237 static const char *const VMAILDIR[] =
  238 {
  239     [PLATFORM_CONTEXT_UNKNOWN] = "-",
  240     [PLATFORM_CONTEXT_OPENVZ] = "/var/spool/mail", /* virt_host_vz_vzps */
  241     [PLATFORM_CONTEXT_HP] = "/var/mail",           /* hpux */
  242     [PLATFORM_CONTEXT_AIX] = "/var/spool/mail",    /* aix */
  243     [PLATFORM_CONTEXT_LINUX] = "/var/spool/mail",  /* linux */
  244     [PLATFORM_CONTEXT_BUSYBOX] = "",               /* linux */
  245     [PLATFORM_CONTEXT_SOLARIS] = "/var/mail",      /* new solaris */
  246     [PLATFORM_CONTEXT_SUN_SOLARIS] = "/var/mail",  /* old solaris */
  247     [PLATFORM_CONTEXT_FREEBSD] = "/var/mail",      /* freebsd */
  248     [PLATFORM_CONTEXT_NETBSD] = "/var/mail",       /* netbsd */
  249     [PLATFORM_CONTEXT_CRAYOS] = "/usr/mail",       /* cray */
  250     [PLATFORM_CONTEXT_WINDOWS_NT] = "N/A",         /* NT */
  251     [PLATFORM_CONTEXT_SYSTEMV] = "/var/mail",      /* Unixware */
  252     [PLATFORM_CONTEXT_OPENBSD] = "/var/mail",      /* openbsd */
  253     [PLATFORM_CONTEXT_CFSCO] = "/var/spool/mail",  /* sco */
  254     [PLATFORM_CONTEXT_DARWIN] = "/var/mail",       /* darwin */
  255     [PLATFORM_CONTEXT_QNX] = "/var/spool/mail",    /* qnx */
  256     [PLATFORM_CONTEXT_DRAGONFLY] = "/var/mail",    /* dragonfly */
  257     [PLATFORM_CONTEXT_MINGW] = "",                 /* mingw */
  258     [PLATFORM_CONTEXT_VMWARE] = "/var/spool/mail", /* vmware */
  259     [PLATFORM_CONTEXT_ANDROID] = "",               /* android */
  260 };
  261 
  262 static const char *const VEXPORTS[] =
  263 {
  264     [PLATFORM_CONTEXT_UNKNOWN] = "-",
  265     [PLATFORM_CONTEXT_OPENVZ] = "/etc/exports",         /* virt_host_vz_vzps */
  266     [PLATFORM_CONTEXT_HP] = "/etc/exports",             /* hpux */
  267     [PLATFORM_CONTEXT_AIX] = "/etc/exports",            /* aix */
  268     [PLATFORM_CONTEXT_LINUX] = "/etc/exports",          /* linux */
  269     [PLATFORM_CONTEXT_BUSYBOX] = "",                    /* linux */
  270     [PLATFORM_CONTEXT_SOLARIS] = "/etc/dfs/dfstab",     /* new solaris */
  271     [PLATFORM_CONTEXT_SUN_SOLARIS] = "/etc/dfs/dfstab", /* old solaris */
  272     [PLATFORM_CONTEXT_FREEBSD] = "/etc/exports",        /* freebsd */
  273     [PLATFORM_CONTEXT_NETBSD] = "/etc/exports",         /* netbsd */
  274     [PLATFORM_CONTEXT_CRAYOS] = "/etc/exports",         /* cray */
  275     [PLATFORM_CONTEXT_WINDOWS_NT] = "/etc/exports",     /* NT */
  276     [PLATFORM_CONTEXT_SYSTEMV] = "/etc/dfs/dfstab",     /* Unixware */
  277     [PLATFORM_CONTEXT_OPENBSD] = "/etc/exports",        /* openbsd */
  278     [PLATFORM_CONTEXT_CFSCO] = "/etc/dfs/dfstab",       /* sco */
  279     [PLATFORM_CONTEXT_DARWIN] = "/etc/exports",         /* darwin */
  280     [PLATFORM_CONTEXT_QNX] = "/etc/exports",            /* qnx */
  281     [PLATFORM_CONTEXT_DRAGONFLY] = "/etc/exports",      /* dragonfly */
  282     [PLATFORM_CONTEXT_MINGW] = "",                      /* mingw */
  283     [PLATFORM_CONTEXT_VMWARE] = "none",                 /* vmware */
  284     [PLATFORM_CONTEXT_ANDROID] = ""  ,                  /* android */
  285 };
  286 
  287 
  288 /*******************************************************************/
  289 
  290 void CalculateDomainName(const char *nodename, const char *dnsname,
  291                          char *fqname, size_t fqname_size,
  292                          char *uqname, size_t uqname_size,
  293                          char *domain, size_t domain_size)
  294 {
  295     if (strstr(dnsname, "."))
  296     {
  297         strlcpy(fqname, dnsname, fqname_size);
  298     }
  299     else
  300     {
  301         strlcpy(fqname, nodename, fqname_size);
  302     }
  303 
  304     if ((strncmp(nodename, fqname, strlen(nodename)) == 0) && (fqname[strlen(nodename)] == '.'))
  305     {
  306         /* If hostname is not qualified */
  307         strlcpy(domain, fqname + strlen(nodename) + 1, domain_size);
  308         strlcpy(uqname, nodename, uqname_size);
  309     }
  310     else
  311     {
  312         /* If hostname is qualified */
  313 
  314         char *p = strchr(nodename, '.');
  315 
  316         if (p != NULL)
  317         {
  318             strlcpy(uqname, nodename, MIN(uqname_size, p - nodename + 1));
  319             strlcpy(domain, p + 1, domain_size);
  320         }
  321         else
  322         {
  323             strlcpy(uqname, nodename, uqname_size);
  324             strlcpy(domain, "", domain_size);
  325         }
  326     }
  327 }
  328 
  329 /*******************************************************************/
  330 
  331 void DetectDomainName(EvalContext *ctx, const char *orig_nodename)
  332 {
  333     char nodename[CF_BUFSIZE];
  334 
  335     strlcpy(nodename, orig_nodename, sizeof(nodename));
  336     ToLowerStrInplace(nodename);
  337 
  338     char dnsname[CF_BUFSIZE] = "";
  339     char fqn[CF_BUFSIZE];
  340 
  341     if (gethostname(fqn, sizeof(fqn)) != -1)
  342     {
  343         struct hostent *hp;
  344 
  345         if ((hp = gethostbyname(fqn)))
  346         {
  347             strlcpy(dnsname, hp->h_name, sizeof(dnsname));
  348             ToLowerStrInplace(dnsname);
  349         }
  350     }
  351 
  352     CalculateDomainName(nodename, dnsname, VFQNAME, CF_MAXVARSIZE,
  353                         VUQNAME, CF_MAXVARSIZE, VDOMAIN, CF_MAXVARSIZE);
  354 
  355 /*
  356  * VFQNAME = a.b.c.d ->
  357  * NewClass("a.b.c.d")
  358  * NewClass("b.c.d")
  359  * NewClass("c.d")
  360  * NewClass("d")
  361  */
  362     char *ptr = VFQNAME;
  363 
  364     do
  365     {
  366         EvalContextClassPutHard(ctx, ptr, "inventory,attribute_name=none,source=agent,derived-from=sys.fqhost");
  367 
  368         ptr = strchr(ptr, '.');
  369         if (ptr != NULL)
  370         {
  371             ptr++;
  372         }
  373     } while (ptr != NULL);
  374 
  375     EvalContextClassPutHard(ctx, VUQNAME, "source=agent,derived-from=sys.uqhost");
  376     EvalContextClassPutHard(ctx, VDOMAIN, "source=agent,derived-from=sys.domain");
  377 
  378     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "host", nodename, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=none");
  379     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "uqhost", VUQNAME, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=none");
  380     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "fqhost", VFQNAME, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=Host name");
  381     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "domain", VDOMAIN, CF_DATA_TYPE_STRING, "source=agent");
  382 }
  383 
  384 /*******************************************************************/
  385 
  386 void DiscoverVersion(EvalContext *ctx)
  387 {
  388     int major = 0;
  389     int minor = 0;
  390     int patch = 0;
  391     char workbuf[CF_BUFSIZE];
  392     if (sscanf(Version(), "%d.%d.%d", &major, &minor, &patch) == 3)
  393     {
  394         snprintf(workbuf, CF_MAXVARSIZE, "%d", major);
  395         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version_major", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  396         snprintf(workbuf, CF_MAXVARSIZE, "%d", minor);
  397         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version_minor", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  398         snprintf(workbuf, CF_MAXVARSIZE, "%d", patch);
  399         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version_patch", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  400     }
  401     else
  402     {
  403         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version_major", "BAD VERSION " VERSION, CF_DATA_TYPE_STRING, "source=agent");
  404         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version_minor", "BAD VERSION " VERSION, CF_DATA_TYPE_STRING, "source=agent");
  405         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version_patch", "BAD VERSION " VERSION, CF_DATA_TYPE_STRING, "source=agent");
  406     }
  407 
  408     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version_release", RELEASE, CF_DATA_TYPE_STRING, "source=agent");
  409 
  410     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "local_libdir", "lib", CF_DATA_TYPE_STRING, "source=agent");
  411 
  412     snprintf(workbuf, CF_BUFSIZE, "%s%cinputs%clib", GetWorkDir(), FILE_SEPARATOR, FILE_SEPARATOR);
  413     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "libdir", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  414 }
  415 
  416 static void GetNameInfo3(EvalContext *ctx)
  417 {
  418     int i, found = false;
  419     char *sp, workbuf[CF_BUFSIZE];
  420     time_t tloc;
  421     struct hostent *hp;
  422     struct sockaddr_in cin;
  423     unsigned char digest[EVP_MAX_MD_SIZE + 1];
  424     const char* const workdir = GetWorkDir();
  425     const char* const bindir = GetBinDir();
  426 
  427 #ifdef _AIX
  428     char real_version[_SYS_NMLN];
  429 #endif
  430 #if defined(HAVE_SYSINFO) && (defined(SI_ARCHITECTURE) || defined(SI_PLATFORM))
  431     long sz;
  432 #endif
  433 
  434 #define COMPONENTS_SIZE 16
  435     // This is used for $(sys.cf_agent), $(sys.cf_serverd) ... :
  436     char *components[COMPONENTS_SIZE] = { "cf-twin", "cf-agent", "cf-serverd", "cf-monitord", "cf-know",
  437         "cf-report", "cf-key", "cf-runagent", "cf-execd", "cf-hub",
  438         "cf-promises", "cf-upgrade", "cf-net", "cf-check", "cf-secret",
  439         NULL
  440     };
  441     int have_component[COMPONENTS_SIZE];
  442     struct stat sb;
  443     char name[CF_MAXVARSIZE], quoteName[CF_MAXVARSIZE], shortname[CF_MAXVARSIZE];
  444 
  445     if (uname(&VSYSNAME) == -1)
  446     {
  447         Log(LOG_LEVEL_ERR, "Couldn't get kernel name info!. (uname: %s)", GetErrorStr());
  448         memset(&VSYSNAME, 0, sizeof(VSYSNAME));
  449     }
  450 
  451 #ifdef _AIX
  452     snprintf(real_version, _SYS_NMLN, "%.80s.%.80s", VSYSNAME.version, VSYSNAME.release);
  453     strlcpy(VSYSNAME.release, real_version, _SYS_NMLN);
  454 #endif
  455 #ifdef __ANDROID__
  456     /*
  457      * uname cannot differentiate android from linux
  458      */
  459     strcpy(VSYSNAME.sysname, "android");
  460 #endif
  461 #ifdef __BUSYBOX__
  462     /*
  463      * uname cannot differentiate a busybox toolset from a normal GNU linux toolset
  464      */
  465      strcpy(VSYSNAME.sysname, "busybox");
  466 #endif
  467 
  468     ToLowerStrInplace(VSYSNAME.sysname);
  469     ToLowerStrInplace(VSYSNAME.machine);
  470 
  471 #ifdef _AIX
  472     switch (_system_configuration.architecture)
  473     {
  474     case POWER_RS:
  475         strlcpy(VSYSNAME.machine, "power", _SYS_NMLN);
  476         break;
  477     case POWER_PC:
  478         strlcpy(VSYSNAME.machine, "powerpc", _SYS_NMLN);
  479         break;
  480     case IA64:
  481         strlcpy(VSYSNAME.machine, "ia64", _SYS_NMLN);
  482         break;
  483     }
  484 #endif
  485 
  486 /*
  487  * solarisx86 is a historically defined class for Solaris on x86. We have to
  488  * define it manually now.
  489  */
  490 #ifdef __sun
  491     if (strcmp(VSYSNAME.machine, "i86pc") == 0)
  492     {
  493         EvalContextClassPutHard(ctx, "solarisx86", "inventory,attribute_name=none,source=agent");
  494     }
  495 #endif
  496 
  497     DetectDomainName(ctx, VSYSNAME.nodename);
  498 
  499     if ((tloc = time((time_t *) NULL)) == -1)
  500     {
  501         Log(LOG_LEVEL_ERR, "Couldn't read system clock");
  502     }
  503     else
  504     {
  505         snprintf(workbuf, CF_BUFSIZE, "%jd", (intmax_t) tloc);
  506         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "systime", workbuf, CF_DATA_TYPE_INT, "time_based,source=agent");
  507         snprintf(workbuf, CF_BUFSIZE, "%jd", (intmax_t) tloc / SECONDS_PER_DAY);
  508         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "sysday", workbuf, CF_DATA_TYPE_INT, "time_based,source=agent");
  509         i = GetUptimeMinutes(tloc);
  510         if (i != -1)
  511         {
  512             snprintf(workbuf, CF_BUFSIZE, "%d", i);
  513             EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "uptime", workbuf, CF_DATA_TYPE_INT, "inventory,time_based,source=agent,attribute_name=Uptime minutes");
  514         }
  515     }
  516 
  517     for (i = 0; i < PLATFORM_CONTEXT_MAX; i++)
  518     {
  519         char sysname[CF_BUFSIZE];
  520         strlcpy(sysname, VSYSNAME.sysname, CF_BUFSIZE);
  521         ToLowerStrInplace(sysname);
  522 
  523         /* FIXME: review those strcmps. Moved out from StringMatch */
  524         if (!strcmp(CLASSATTRIBUTES[i][0], sysname)
  525             || StringMatchFull(CLASSATTRIBUTES[i][0], sysname))
  526         {
  527             if (!strcmp(CLASSATTRIBUTES[i][1], VSYSNAME.machine)
  528                 || StringMatchFull(CLASSATTRIBUTES[i][1], VSYSNAME.machine))
  529             {
  530                 if (!strcmp(CLASSATTRIBUTES[i][2], VSYSNAME.release)
  531                     || StringMatchFull(CLASSATTRIBUTES[i][2], VSYSNAME.release))
  532                 {
  533                     EvalContextClassPutHard(ctx, CLASSTEXT[i], "inventory,attribute_name=none,source=agent,derived-from=sys.class");
  534 
  535                     found = true;
  536 
  537                     VSYSTEMHARDCLASS = (PlatformContext) i;
  538                     VPSHARDCLASS = (PlatformContext) i; /* this one can be overriden at vz detection */
  539                     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "class", CLASSTEXT[i], CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=OS type");
  540                     break;
  541                 }
  542             }
  543             else
  544             {
  545                 Log(LOG_LEVEL_DEBUG, "I recognize '%s' but not '%s'", VSYSNAME.sysname, VSYSNAME.machine);
  546                 continue;
  547             }
  548         }
  549     }
  550 
  551     if (!found)
  552     {
  553         i = 0;
  554     }
  555 
  556     Log(LOG_LEVEL_VERBOSE, "%s - ready", NameVersion());
  557     Banner("Environment discovery");
  558 
  559     snprintf(workbuf, CF_BUFSIZE, "%s", CLASSTEXT[i]);
  560 
  561 
  562     Log(LOG_LEVEL_VERBOSE, "Host name is: %s", VSYSNAME.nodename);
  563     Log(LOG_LEVEL_VERBOSE, "Operating System Type is %s", VSYSNAME.sysname);
  564     Log(LOG_LEVEL_VERBOSE, "Operating System Release is %s", VSYSNAME.release);
  565     Log(LOG_LEVEL_VERBOSE, "Architecture = %s", VSYSNAME.machine);
  566     Log(LOG_LEVEL_VERBOSE, "CFEngine detected operating system description is %s", workbuf);
  567     Log(LOG_LEVEL_VERBOSE, "The time is now %s", ctime(&tloc));
  568 
  569     snprintf(workbuf, CF_MAXVARSIZE, "%s", ctime(&tloc));
  570     Chop(workbuf, CF_EXPANDSIZE);
  571 
  572     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "date", workbuf, CF_DATA_TYPE_STRING, "time_based,source=agent");
  573     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cdate", CanonifyName(workbuf), CF_DATA_TYPE_STRING, "time_based,source=agent");
  574     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "os", VSYSNAME.sysname, CF_DATA_TYPE_STRING, "source=agent");
  575     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "release", VSYSNAME.release, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=OS kernel");
  576     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "version", VSYSNAME.version, CF_DATA_TYPE_STRING, "source=agent");
  577     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "arch", VSYSNAME.machine, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=Architecture");
  578     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "workdir", workdir, CF_DATA_TYPE_STRING, "source=agent");
  579     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "fstab", VFSTAB[VSYSTEMHARDCLASS], CF_DATA_TYPE_STRING, "source=agent");
  580     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "resolv", VRESOLVCONF[VSYSTEMHARDCLASS], CF_DATA_TYPE_STRING, "source=agent");
  581     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "maildir", VMAILDIR[VSYSTEMHARDCLASS], CF_DATA_TYPE_STRING, "source=agent");
  582     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "exports", VEXPORTS[VSYSTEMHARDCLASS], CF_DATA_TYPE_STRING, "source=agent");
  583     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "logdir", GetLogDir(), CF_DATA_TYPE_STRING, "source=agent");
  584     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "piddir", GetPidDir(), CF_DATA_TYPE_STRING, "source=agent");
  585     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "statedir", GetStateDir(), CF_DATA_TYPE_STRING, "source=agent");
  586     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "masterdir", GetMasterDir(), CF_DATA_TYPE_STRING, "source=agent");
  587     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "inputdir", GetInputDir(), CF_DATA_TYPE_STRING, "source=agent");
  588 
  589     snprintf(workbuf, CF_BUFSIZE, "%s", bindir);
  590     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "bindir", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  591 
  592     snprintf(workbuf, CF_BUFSIZE, "%s%cpromises.cf", GetInputDir(), FILE_SEPARATOR);
  593     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "default_policy_path", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  594 
  595     snprintf(workbuf, CF_BUFSIZE, "%s%cfailsafe.cf", GetInputDir(), FILE_SEPARATOR);
  596     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "failsafe_policy_path", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  597 
  598     snprintf(workbuf, CF_BUFSIZE, "%s%cupdate.cf", GetInputDir(), FILE_SEPARATOR);
  599     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "update_policy_path", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  600 
  601 /* FIXME: type conversion */
  602     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cf_version", (char *) Version(), CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=CFEngine version");
  603 
  604     DiscoverVersion(ctx);
  605 
  606     if (PUBKEY)
  607     {
  608         char pubkey_digest[CF_HOSTKEY_STRING_SIZE] = { 0 };
  609 
  610         HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST);
  611         HashPrintSafe(pubkey_digest, sizeof(pubkey_digest), digest,
  612                       CF_DEFAULT_DIGEST, true);
  613 
  614         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "key_digest", pubkey_digest, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=CFEngine ID");
  615 
  616         snprintf(workbuf, CF_MAXVARSIZE - 1, "PK_%s", pubkey_digest);
  617         CanonifyNameInPlace(workbuf);
  618         EvalContextClassPutHard(ctx, workbuf, "inventory,attribute_name=none,source=agent,derived-from=sys.key_digest");
  619     }
  620 
  621     for (i = 0; components[i] != NULL; i++)
  622     {
  623         snprintf(shortname, CF_MAXVARSIZE - 1, "%s", CanonifyName(components[i]));
  624 
  625 #if defined(_WIN32)
  626         // twin has own dir, and is named agent
  627         if (i == 0)
  628         {
  629             snprintf(name, CF_MAXVARSIZE - 1, "%s-twin%ccf-agent.exe", bindir, FILE_SEPARATOR);
  630         }
  631         else
  632         {
  633             snprintf(name, CF_MAXVARSIZE - 1, "%s%c%s.exe", bindir, FILE_SEPARATOR, components[i]);
  634         }
  635 #else
  636         snprintf(name, CF_MAXVARSIZE - 1, "%s%c%s", bindir, FILE_SEPARATOR, components[i]);
  637 #endif
  638 
  639         have_component[i] = false;
  640 
  641         if (stat(name, &sb) != -1)
  642         {
  643             snprintf(quoteName, sizeof(quoteName), "\"%s\"", name);
  644             // Sets $(sys.cf_agent), $(sys.cf_serverd) $(sys.cf_execd) etc.
  645             // to their respective /bin paths
  646             EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, shortname, quoteName, CF_DATA_TYPE_STRING, "cfe_internal,source=agent");
  647             have_component[i] = true;
  648         }
  649     }
  650 
  651 // If no twin, fail over the agent
  652 
  653     if (!have_component[0])
  654     {
  655         snprintf(shortname, CF_MAXVARSIZE - 1, "%s", CanonifyName(components[0]));
  656 
  657 #if defined(_WIN32)
  658         snprintf(name, CF_MAXVARSIZE - 1, "%s%c%s.exe", bindir, FILE_SEPARATOR,
  659                  components[1]);
  660 #else
  661         snprintf(name, CF_MAXVARSIZE - 1, "%s%c%s", bindir, FILE_SEPARATOR, components[1]);
  662 #endif
  663 
  664         if (stat(name, &sb) != -1)
  665         {
  666             snprintf(quoteName, sizeof(quoteName), "\"%s\"", name);
  667             EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, shortname, quoteName, CF_DATA_TYPE_STRING, "cfe_internal,source=agent");
  668         }
  669     }
  670 
  671 /* Windows special directories and tools */
  672 
  673 #ifdef __MINGW32__
  674     if (NovaWin_GetWinDir(workbuf, sizeof(workbuf)))
  675     {
  676         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "windir", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  677     }
  678 
  679     if (NovaWin_GetSysDir(workbuf, sizeof(workbuf)))
  680     {
  681         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "winsysdir", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  682 
  683         char filename[CF_BUFSIZE];
  684         if (snprintf(filename, sizeof(filename), "%s%s", workbuf, "\\WindowsPowerShell\\v1.0\\powershell.exe") < sizeof(filename))
  685         {
  686             if (NovaWin_FileExists(filename))
  687             {
  688                 EvalContextClassPutHard(ctx, "powershell", "inventory,attribute_name=none,source=agent");
  689                 Log(LOG_LEVEL_VERBOSE, "Additional hard class defined as: %s", "powershell");
  690             }
  691         }
  692     }
  693 
  694     if (NovaWin_GetProgDir(workbuf, sizeof(workbuf)))
  695     {
  696         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "winprogdir", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  697     }
  698 
  699 # ifdef _WIN64
  700 // only available on 64 bit windows systems
  701     if (NovaWin_GetEnv("PROGRAMFILES(x86)", workbuf, sizeof(workbuf)))
  702     {
  703         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "winprogdir86", workbuf, CF_DATA_TYPE_STRING, "source=agent");
  704     }
  705 
  706 # else/* NOT _WIN64 */
  707 
  708     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "winprogdir86", "", CF_DATA_TYPE_STRING, "source=agent");
  709 
  710 # endif
  711 #endif /* !__MINGW32__ */
  712 
  713     EnterpriseContext(ctx);
  714 
  715     snprintf(workbuf, sizeof(workbuf), "%u_bit", (unsigned) sizeof(void*) * 8);
  716     EvalContextClassPutHard(ctx, workbuf, "source=agent");
  717     Log(LOG_LEVEL_VERBOSE, "Additional hard class defined as: %s", CanonifyName(workbuf));
  718 
  719     snprintf(workbuf, CF_BUFSIZE, "%s_%s", VSYSNAME.sysname, VSYSNAME.release);
  720     EvalContextClassPutHard(ctx, workbuf, "inventory,attribute_name=none,source=agent,derived-from=sys.sysname,derived-from=sys.release");
  721 
  722     EvalContextClassPutHard(ctx, VSYSNAME.machine, "source=agent,derived-from=sys.machine");
  723     Log(LOG_LEVEL_VERBOSE, "Additional hard class defined as: %s", CanonifyName(workbuf));
  724 
  725     snprintf(workbuf, CF_BUFSIZE, "%s_%s", VSYSNAME.sysname, VSYSNAME.machine);
  726     EvalContextClassPutHard(ctx, workbuf, "source=agent,derived-from=sys.sysname,derived-from=sys.machine");
  727     Log(LOG_LEVEL_VERBOSE, "Additional hard class defined as: %s", CanonifyName(workbuf));
  728 
  729     snprintf(workbuf, CF_BUFSIZE, "%s_%s_%s", VSYSNAME.sysname, VSYSNAME.machine, VSYSNAME.release);
  730     EvalContextClassPutHard(ctx, workbuf, "source=agent,derived-from=sys.sysname,derived-from=sys.machine,derived-from=sys.release");
  731     Log(LOG_LEVEL_VERBOSE, "Additional hard class defined as: %s", CanonifyName(workbuf));
  732 
  733 #ifdef HAVE_SYSINFO
  734 # ifdef SI_ARCHITECTURE
  735     sz = sysinfo(SI_ARCHITECTURE, workbuf, CF_BUFSIZE);
  736     if (sz == -1)
  737     {
  738         Log(LOG_LEVEL_VERBOSE, "cfengine internal: sysinfo returned -1");
  739     }
  740     else
  741     {
  742         EvalContextClassPutHard(ctx, workbuf, "inventory,attribute_name=none,source=agent");
  743         Log(LOG_LEVEL_VERBOSE, "Additional hard class defined as: %s", workbuf);
  744     }
  745 # endif
  746 # ifdef SI_PLATFORM
  747     sz = sysinfo(SI_PLATFORM, workbuf, CF_BUFSIZE);
  748     if (sz == -1)
  749     {
  750         Log(LOG_LEVEL_VERBOSE, "cfengine internal: sysinfo returned -1");
  751     }
  752     else
  753     {
  754         EvalContextClassPutHard(ctx, workbuf, "inventory,attribute_name=none,source=agent");
  755         Log(LOG_LEVEL_VERBOSE, "Additional hard class defined as: %s", workbuf);
  756     }
  757 # endif
  758 #endif
  759 
  760     snprintf(workbuf, CF_BUFSIZE, "%s_%s_%s_%s", VSYSNAME.sysname, VSYSNAME.machine, VSYSNAME.release,
  761              VSYSNAME.version);
  762 
  763     if (strlen(workbuf) > CF_MAXVARSIZE - 2)
  764     {
  765         Log(LOG_LEVEL_VERBOSE, "cfengine internal: $(arch) overflows CF_MAXVARSIZE! Truncating");
  766     }
  767 
  768     sp = xstrdup(CanonifyName(workbuf));
  769     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "long_arch", sp, CF_DATA_TYPE_STRING, "source=agent");
  770     EvalContextClassPutHard(ctx, sp, "source=agent,derived-from=sys.long_arch");
  771     free(sp);
  772 
  773     snprintf(workbuf, CF_BUFSIZE, "%s_%s", VSYSNAME.sysname, VSYSNAME.machine);
  774     sp = xstrdup(CanonifyName(workbuf));
  775     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "ostype", sp, CF_DATA_TYPE_STRING, "source=agent");
  776     EvalContextClassPutHard(ctx, sp, "inventory,attribute_name=none,source=agent,derived-from=sys.ostype");
  777     free(sp);
  778 
  779     if (!found)
  780     {
  781         Log(LOG_LEVEL_ERR, "I don't understand what architecture this is");
  782     }
  783 
  784     char compile_str[] = "compiled_on_";
  785     size_t compile_str_len = sizeof(compile_str);
  786     strcpy(workbuf, compile_str);
  787 
  788     strlcat(workbuf, CanonifyName(AUTOCONF_SYSNAME),
  789             CF_BUFSIZE - compile_str_len);
  790     EvalContextClassPutHard(ctx, workbuf, "source=agent");
  791     Log(LOG_LEVEL_VERBOSE, "GNU autoconf class from compile time: %s", workbuf);
  792 
  793 /* Get IP address from nameserver */
  794 
  795     if ((hp = gethostbyname(VFQNAME)) == NULL)
  796     {
  797         Log(LOG_LEVEL_VERBOSE, "Hostname lookup failed on node name '%s'", VSYSNAME.nodename);
  798         return;
  799     }
  800     else
  801     {
  802         memset(&cin, 0, sizeof(cin));
  803         cin.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr;
  804         Log(LOG_LEVEL_VERBOSE, "Address given by nameserver: %s", inet_ntoa(cin.sin_addr));
  805         strcpy(VIPADDRESS, inet_ntoa(cin.sin_addr));
  806 
  807         for (i = 0; hp->h_aliases[i] != NULL; i++)
  808         {
  809             Log(LOG_LEVEL_DEBUG, "Adding alias '%s'", hp->h_aliases[i]);
  810             EvalContextClassPutHard(ctx, hp->h_aliases[i], "inventory,attribute_name=none,source=agent,based-on=sys.fqhost");
  811         }
  812     }
  813 
  814 #ifdef HAVE_GETZONEID
  815     zoneid_t zid;
  816     char zone[ZONENAME_MAX];
  817     char vbuff[CF_BUFSIZE];
  818 
  819     zid = getzoneid();
  820     getzonenamebyid(zid, zone, ZONENAME_MAX);
  821 
  822     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "zone", zone, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=Solaris zone");
  823     snprintf(vbuff, CF_BUFSIZE - 1, "zone_%s", zone);
  824     EvalContextClassPutHard(ctx, vbuff, "source=agent,derived-from=sys.zone");
  825 
  826     if (strcmp(zone, "global") == 0)
  827     {
  828         Log(LOG_LEVEL_VERBOSE, "CFEngine seems to be running inside a global solaris zone of name '%s'", zone);
  829     }
  830     else
  831     {
  832         Log(LOG_LEVEL_VERBOSE, "CFEngine seems to be running inside a local solaris zone of name '%s'", zone);
  833     }
  834 #endif
  835 }
  836 
  837 /*******************************************************************/
  838 
  839 void LoadSlowlyVaryingObservations(EvalContext *ctx)
  840 {
  841     CF_DB *dbp;
  842     CF_DBC *dbcp;
  843     char *key;
  844     void *stored;
  845     int ksize, vsize;
  846 
  847     if (!OpenDB(&dbp, dbid_static))
  848     {
  849         return;
  850     }
  851 
  852     /* Acquire a cursor for the database. */
  853     if (!NewDBCursor(dbp, &dbcp))
  854     {
  855         Log(LOG_LEVEL_INFO, "Unable to scan class db");
  856         CloseDB(dbp);
  857         return;
  858     }
  859 
  860     while (NextDB(dbcp, &key, &ksize, &stored, &vsize))
  861     {
  862         if (key == NULL  ||  stored == NULL)
  863         {
  864             continue;
  865         }
  866 
  867         char lval[1024];
  868         int type_i;
  869         int ret = sscanf(key, "%1023[^:]:%d", lval, &type_i);
  870         if (ret == 2)
  871         {
  872             DataType type = type_i;
  873             switch (type)
  874             {
  875             case CF_DATA_TYPE_STRING:
  876             case CF_DATA_TYPE_INT:
  877             case CF_DATA_TYPE_REAL:
  878                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_MON,
  879                                               lval, stored, type,
  880                                               "monitoring,source=observation");
  881                 break;
  882 
  883             case CF_DATA_TYPE_STRING_LIST:
  884             {
  885                 Rlist *list = RlistFromSplitString(stored, ',');
  886                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_MON,
  887                                               lval, list, CF_DATA_TYPE_STRING_LIST,
  888                                               "monitoring,source=observation");
  889                 RlistDestroy(list);
  890                 break;
  891             }
  892             case CF_DATA_TYPE_COUNTER:
  893                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_MON,
  894                                               lval, stored, CF_DATA_TYPE_STRING,
  895                                               "monitoring,source=observation");
  896                 break;
  897 
  898             default:
  899                 Log(LOG_LEVEL_ERR,
  900                     "Unexpected monitoring type %d found in dbid_static database",
  901                     (int) type);
  902             }
  903         }
  904     }
  905 
  906     DeleteDBCursor(dbcp);
  907     CloseDB(dbp);
  908 }
  909 
  910 static void Get3Environment(EvalContext *ctx)
  911 {
  912     char env[CF_BUFSIZE], context[CF_BUFSIZE], name[CF_MAXVARSIZE], value[CF_BUFSIZE];
  913     struct stat statbuf;
  914     time_t now = time(NULL);
  915 
  916     Log(LOG_LEVEL_VERBOSE, "Looking for environment from cf-monitord...");
  917 
  918     snprintf(env, CF_BUFSIZE, "%s/%s", GetStateDir(), CF_ENV_FILE);
  919     MapName(env);
  920 
  921     FILE *fp = safe_fopen(env, "r");
  922     if (fp == NULL)
  923     {
  924         Log(LOG_LEVEL_VERBOSE, "Unable to detect environment from cf-monitord");
  925         return;
  926     }
  927 
  928     int fd = fileno(fp);
  929     if (fstat(fd, &statbuf) == -1)
  930     {
  931         Log(LOG_LEVEL_VERBOSE, "Unable to detect environment from cf-monitord");
  932         fclose(fp);
  933         return;
  934     }
  935 
  936     if (statbuf.st_mtime < (now - 60 * 60))
  937     {
  938         Log(LOG_LEVEL_VERBOSE, "Environment data are too old - discarding");
  939         unlink(env);
  940         fclose(fp);
  941         return;
  942     }
  943 
  944     snprintf(value, CF_MAXVARSIZE - 1, "%s", ctime(&statbuf.st_mtime));
  945     if (Chop(value, CF_EXPANDSIZE) == -1)
  946     {
  947         Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator");
  948     }
  949 
  950     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_MON, "env_time", value, CF_DATA_TYPE_STRING, "time_based,source=agent");
  951 
  952     Log(LOG_LEVEL_VERBOSE, "Loading environment...");
  953 
  954     for(;;)
  955     {
  956         name[0] = '\0';
  957         value[0] = '\0';
  958 
  959         if (fgets(context, sizeof(context), fp) == NULL)
  960         {
  961             if (ferror(fp))
  962             {
  963                 UnexpectedError("Failed to read line from stream");
  964                 break;
  965             }
  966             else /* feof */
  967             {
  968                 break;
  969             }
  970         }
  971 
  972 
  973         if (*context == '@')
  974         {
  975             if (sscanf(context + 1, "%[^=]=%[^\n]", name, value) == 2)
  976             {
  977                 Log(LOG_LEVEL_DEBUG,
  978                     "Setting new monitoring list '%s' => '%s'",
  979                     name, value);
  980                 Rlist *list = RlistParseShown(value);
  981                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_MON,
  982                                               name, list,
  983                                               CF_DATA_TYPE_STRING_LIST,
  984                                               "monitoring,source=environment");
  985 
  986                 RlistDestroy(list);
  987             }
  988             else
  989             {
  990                 Log(LOG_LEVEL_ERR,
  991                     "Failed to parse '%s' as '@variable=list' monitoring list",
  992                     context);
  993             }
  994         }
  995         else if (strchr(context, '='))
  996         {
  997             if (sscanf(context, "%255[^=]=%255[^\n]", name, value) == 2)
  998             {
  999                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_MON,
 1000                                               name, value,
 1001                                               CF_DATA_TYPE_STRING,
 1002                                               "monitoring,source=environment");
 1003                 Log(LOG_LEVEL_DEBUG,
 1004                     "Setting new monitoring scalar '%s' => '%s'",
 1005                     name, value);
 1006             }
 1007             else
 1008             {
 1009                 Log(LOG_LEVEL_ERR,
 1010                     "Failed to parse '%s' as 'variable=value' monitoring scalar",
 1011                     context);
 1012             }
 1013         }
 1014         else
 1015         {
 1016             StripTrailingNewline(context, CF_BUFSIZE);
 1017             EvalContextClassPutHard(ctx, context, "monitoring,source=environment");
 1018         }
 1019     }
 1020 
 1021     fclose(fp);
 1022     Log(LOG_LEVEL_VERBOSE, "Environment data loaded");
 1023 
 1024     LoadSlowlyVaryingObservations(ctx);
 1025 }
 1026 
 1027 static void BuiltinClasses(EvalContext *ctx)
 1028 {
 1029     char vbuff[CF_BUFSIZE];
 1030 
 1031     EvalContextClassPutHard(ctx, "any", "source=agent");            /* This is a reserved word / wildcard */
 1032 
 1033     snprintf(vbuff, CF_BUFSIZE, "cfengine_%s", CanonifyName(Version()));
 1034     CreateHardClassesFromCanonification(ctx, vbuff, "inventory,attribute_name=none,source=agent");
 1035 
 1036     CreateHardClassesFromFeatures(ctx, "source=agent");
 1037 }
 1038 
 1039 /*******************************************************************/
 1040 
 1041 void CreateHardClassesFromCanonification(EvalContext *ctx, const char *canonified, char *tags)
 1042 {
 1043     char buf[CF_MAXVARSIZE];
 1044 
 1045     strlcpy(buf, canonified, sizeof(buf));
 1046 
 1047     EvalContextClassPutHard(ctx, buf, tags);
 1048 
 1049     char *sp;
 1050 
 1051     while ((sp = strrchr(buf, '_')))
 1052     {
 1053         *sp = 0;
 1054         EvalContextClassPutHard(ctx, buf, tags);
 1055     }
 1056 }
 1057 
 1058 static void SetFlavor(EvalContext *ctx, const char *flavor)
 1059 {
 1060     EvalContextClassPutHard(ctx, flavor, "inventory,attribute_name=none,source=agent,derived-from=sys.flavor");
 1061     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "flavour", flavor, CF_DATA_TYPE_STRING, "source=agent");
 1062     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "flavor", flavor, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=none");
 1063 }
 1064 
 1065 #ifdef __linux__
 1066 
 1067 /**
 1068  * @brief Combines OS and version string to define flavor variable and class
 1069  *
 1070  * @note Input strings should be canonified before calling
 1071  */
 1072 static void SetFlavor2(
 1073     EvalContext *const ctx,
 1074     const char *const id,
 1075     const char *const major_version)
 1076 {
 1077     assert(ctx != NULL);
 1078     assert(id != NULL);
 1079     assert(major_version != NULL);
 1080 
 1081     char *flavor;
 1082     xasprintf(&flavor, "%s_%s", id, major_version);
 1083     SetFlavor(ctx, flavor);
 1084     free(flavor);
 1085 }
 1086 
 1087 /**
 1088  * @brief Combines OS and version string to define multiple hard classes
 1089  *
 1090  * @note Input strings should be canonified before calling
 1091  */
 1092 static void DefineVersionedHardClasses(
 1093     EvalContext *const ctx,
 1094     const char *const tags,
 1095     const char *const id,
 1096     const char *const version)
 1097 {
 1098     assert(ctx != NULL);
 1099     assert(id != NULL);
 1100     assert(version != NULL);
 1101 
 1102     char *class;
 1103     xasprintf(&class, "%s_%s", id, version);
 1104 
 1105     // Strip away version number to define multiple hard classes
 1106     // Example: coreos_1185_3_0 -> coreos_1185_3 -> coreos_1185 -> coreos
 1107     char *last_underscore = strrchr(class, '_');
 1108     while ( last_underscore != NULL )
 1109     {
 1110         EvalContextClassPutHard(ctx, class, tags);
 1111         *last_underscore = '\0';
 1112         last_underscore = strrchr(class, '_');
 1113     }
 1114     EvalContextClassPutHard(ctx, class, tags);
 1115     free(class);
 1116 }
 1117 
 1118 static void OSReleaseParse(EvalContext *ctx, const char *file_path)
 1119 {
 1120     JsonElement *os_release_json = JsonReadDataFile("system info discovery",
 1121                                                     file_path, DATAFILETYPE_ENV,
 1122                                                     100 * 1024);
 1123     if (os_release_json != NULL)
 1124     {
 1125         char *tags;
 1126         xasprintf(&tags,
 1127                   "inventory,attribute_name=none,source=agent,derived-from-file=%s",
 1128                   file_path);
 1129         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "os_release",
 1130                                       os_release_json, CF_DATA_TYPE_CONTAINER,
 1131                                       tags);
 1132         const char *const_os_release_id = JsonObjectGetAsString(os_release_json, "ID");
 1133         const char *const_os_release_version_id = JsonObjectGetAsString(os_release_json, "VERSION_ID");
 1134         char *os_release_id = SafeStringDuplicate(const_os_release_id);
 1135         char *os_release_version_id = SafeStringDuplicate(const_os_release_version_id);
 1136 
 1137         if (os_release_id != NULL)
 1138         {
 1139             CanonifyNameInPlace(os_release_id);
 1140 
 1141             const char *alias = NULL;
 1142             if (StringEqual(os_release_id, "rhel"))
 1143             {
 1144                 alias = "redhat";
 1145             }
 1146             else if (StringEqual(os_release_id, "opensuse") || 
 1147                      StringEqual(os_release_id, "sles"))
 1148             {
 1149                 alias = "suse";
 1150             }
 1151 
 1152             if (os_release_version_id == NULL)
 1153             {
 1154                 // if VERSION_ID doesn't exist, define only one hard class:
 1155                 EvalContextClassPutHard(ctx, os_release_id, tags);
 1156                 if (alias != NULL)
 1157                 {
 1158                     EvalContextClassPutHard(ctx, alias, tags);
 1159                 }
 1160             }
 1161             else // if VERSION_ID exists set flavor and multiple hard classes:
 1162             {
 1163                 CanonifyNameInPlace(os_release_version_id);
 1164 
 1165                 // Set the flavor to be ID + major version (derived from VERSION_ID)
 1166                 char *first_underscore = strchr(os_release_version_id, '_');
 1167                 if (first_underscore != NULL)
 1168                 {
 1169                     // Temporarily modify os_release_version_id to be major version
 1170                     *first_underscore = '\0';
 1171                     SetFlavor2(ctx, os_release_id, os_release_version_id);
 1172                     *first_underscore = '_';
 1173                 }
 1174                 else
 1175                 {
 1176                     SetFlavor2(ctx, os_release_id, os_release_version_id);
 1177                 }
 1178 
 1179                 // One of the hard classes is already set by SetFlavor
 1180                 // but it seems excessive to try to skip this:
 1181                 DefineVersionedHardClasses(ctx, tags, os_release_id, os_release_version_id);
 1182                 if (alias != NULL)
 1183                 {
 1184                     DefineVersionedHardClasses(ctx, tags, alias, os_release_version_id);
 1185                 }
 1186             }
 1187         }
 1188         free(os_release_version_id);
 1189         free(os_release_id);
 1190         free(tags);
 1191         JsonDestroy(os_release_json);
 1192     }
 1193 }
 1194 #endif
 1195 
 1196 static void OSClasses(EvalContext *ctx)
 1197 {
 1198 #ifdef __linux__
 1199 
 1200 /* First we check if init process is systemd, and set "systemd" hard class. */
 1201 
 1202     {
 1203         char init_path[CF_BUFSIZE];
 1204         if (ReadLine("/proc/1/cmdline", init_path, sizeof(init_path)))
 1205         {
 1206             /* Follow possible symlinks. */
 1207 
 1208             char resolved_path[PATH_MAX];      /* realpath() needs PATH_MAX */
 1209             if (realpath(init_path, resolved_path) != NULL &&
 1210                 strlen(resolved_path) < sizeof(init_path))
 1211             {
 1212                 strcpy(init_path, resolved_path);
 1213             }
 1214 
 1215             /* Check if string ends with "/systemd". */
 1216             char *p;
 1217             char *next_p = NULL;
 1218             const char *term = "/systemd";
 1219             do
 1220             {
 1221                 p = next_p;
 1222                 next_p = strstr(next_p ? next_p+strlen(term) : init_path, term);
 1223             }
 1224             while (next_p);
 1225 
 1226             if (p != NULL &&
 1227                 p[strlen("/systemd")] == '\0')
 1228             {
 1229                 EvalContextClassPutHard(ctx, "systemd",
 1230                                         "inventory,attribute_name=none,source=agent");
 1231             }
 1232         }
 1233     }
 1234 
 1235 
 1236     struct stat statbuf;
 1237 
 1238     // os-release is used to set sys.os_release, sys.flavor and hard classes
 1239     if (access("/etc/os-release", R_OK) != -1)
 1240     {
 1241         OSReleaseParse(ctx, "/etc/os-release");
 1242     }
 1243     else if (access("/usr/lib/os-release", R_OK) != -1)
 1244     {
 1245         OSReleaseParse(ctx, "/usr/lib/os-release");
 1246     }
 1247 
 1248 /* Mandrake/Mandriva, Fedora and Oracle VM Server supply /etc/redhat-release, so
 1249    we test for those distributions first */
 1250 
 1251     if (stat("/etc/mandriva-release", &statbuf) != -1)
 1252     {
 1253         Linux_Mandriva_Version(ctx);
 1254     }
 1255     else if (stat("/etc/mandrake-release", &statbuf) != -1)
 1256     {
 1257         Linux_Mandrake_Version(ctx);
 1258     }
 1259     else if (stat("/etc/fedora-release", &statbuf) != -1)
 1260     {
 1261         Linux_Fedora_Version(ctx);
 1262     }
 1263     else if (stat("/etc/ovs-release", &statbuf) != -1)
 1264     {
 1265         Linux_Oracle_VM_Server_Version(ctx);
 1266     }
 1267     else if (stat("/etc/redhat-release", &statbuf) != -1)
 1268     {
 1269         Linux_Redhat_Version(ctx);
 1270     }
 1271 
 1272 /* Oracle Linux >= 6 supplies separate /etc/oracle-release alongside
 1273    /etc/redhat-release, use it to precisely identify version */
 1274 
 1275     if (stat("/etc/oracle-release", &statbuf) != -1)
 1276     {
 1277         Linux_Oracle_Version(ctx);
 1278     }
 1279 
 1280     if (stat("/etc/generic-release", &statbuf) != -1)
 1281     {
 1282         Log(LOG_LEVEL_VERBOSE, "This appears to be a sun cobalt system.");
 1283         SetFlavor(ctx, "SunCobalt");
 1284     }
 1285 
 1286     if (stat("/etc/SuSE-release", &statbuf) != -1)
 1287     {
 1288         Linux_Suse_Version(ctx);
 1289     }
 1290 
 1291     if (stat("/etc/system-release", &statbuf) != -1)
 1292     {
 1293         Linux_Amazon_Version(ctx);
 1294     }
 1295 
 1296 # define SLACKWARE_ANCIENT_VERSION_FILENAME "/etc/slackware-release"
 1297 # define SLACKWARE_VERSION_FILENAME "/etc/slackware-version"
 1298     if (stat(SLACKWARE_VERSION_FILENAME, &statbuf) != -1)
 1299     {
 1300         Linux_Slackware_Version(ctx, SLACKWARE_VERSION_FILENAME);
 1301     }
 1302     else if (stat(SLACKWARE_ANCIENT_VERSION_FILENAME, &statbuf) != -1)
 1303     {
 1304         Linux_Slackware_Version(ctx, SLACKWARE_ANCIENT_VERSION_FILENAME);
 1305     }
 1306 
 1307     if (stat(DEBIAN_VERSION_FILENAME, &statbuf) != -1)
 1308     {
 1309         Linux_Debian_Version(ctx);
 1310     }
 1311 
 1312     if (stat(LSB_RELEASE_FILENAME, &statbuf) != -1)
 1313     {
 1314         Linux_Misc_Version(ctx);
 1315     }
 1316 
 1317     if (stat("/usr/bin/aptitude", &statbuf) != -1)
 1318     {
 1319         Log(LOG_LEVEL_VERBOSE, "This system seems to have the aptitude package system");
 1320         EvalContextClassPutHard(ctx, "have_aptitude", "inventory,attribute_name=none,source=agent");
 1321     }
 1322 
 1323     if (stat("/etc/UnitedLinux-release", &statbuf) != -1)
 1324     {
 1325         Log(LOG_LEVEL_VERBOSE, "This appears to be a UnitedLinux system.");
 1326         SetFlavor(ctx, "UnitedLinux");
 1327     }
 1328 
 1329     if (stat("/etc/alpine-release", &statbuf) != -1)
 1330     {
 1331         Linux_Alpine_Version(ctx);
 1332     }
 1333 
 1334     if (stat("/etc/gentoo-release", &statbuf) != -1)
 1335     {
 1336         Log(LOG_LEVEL_VERBOSE, "This appears to be a gentoo system.");
 1337         SetFlavor(ctx, "gentoo");
 1338     }
 1339 
 1340     if (stat("/etc/arch-release", &statbuf) != -1)
 1341     {
 1342         Log(LOG_LEVEL_VERBOSE, "This appears to be an Arch Linux system.");
 1343         SetFlavor(ctx, "archlinux");
 1344     }
 1345 
 1346     if (stat("/proc/vmware/version", &statbuf) != -1 || stat("/etc/vmware-release", &statbuf) != -1)
 1347     {
 1348         VM_Version(ctx);
 1349     }
 1350     else if (stat("/etc/vmware", &statbuf) != -1 && S_ISDIR(statbuf.st_mode))
 1351     {
 1352         VM_Version(ctx);
 1353     }
 1354 
 1355     if (stat("/proc/xen/capabilities", &statbuf) != -1)
 1356     {
 1357         Xen_Domain(ctx);
 1358     }
 1359     if (stat("/etc/Eos-release", &statbuf) != -1)
 1360     {
 1361         EOS_Version(ctx);
 1362         SetFlavor(ctx, "Eos");
 1363     }
 1364 
 1365     if (stat("/etc/issue", &statbuf) != -1)
 1366     {
 1367         MiscOS(ctx);
 1368     }
 1369 
 1370     if (stat("/proc/self/status", &statbuf) != -1)
 1371     {
 1372         OpenVZ_Detect(ctx);
 1373     }
 1374 
 1375 #else
 1376 
 1377     char vbuff[CF_MAXVARSIZE];
 1378 
 1379 #ifdef _AIX
 1380     strlcpy(vbuff, VSYSNAME.version, CF_MAXVARSIZE);
 1381 #else
 1382     strlcpy(vbuff, VSYSNAME.release, CF_MAXVARSIZE);
 1383 #endif
 1384 
 1385 
 1386     for (char *sp = vbuff; *sp != '\0'; sp++)
 1387     {
 1388         if (*sp == '-')
 1389         {
 1390             *sp = '\0';
 1391             break;
 1392         }
 1393     }
 1394 
 1395     char context[CF_BUFSIZE];
 1396     snprintf(context, CF_BUFSIZE, "%s_%s", VSYSNAME.sysname, vbuff);
 1397     SetFlavor(ctx, context);
 1398 
 1399 
 1400 #ifdef __hpux
 1401     /*
 1402      * Define a hard class with just the version major number of HP-UX
 1403      *
 1404      * For example, when being run on HP-UX B.11.23 the following class will
 1405      * be defined: hpux_11
 1406      */
 1407 
 1408     // Extract major version number
 1409     char *major = NULL;
 1410     for (char *sp = vbuff; *sp != '\0'; sp++)
 1411     {
 1412         if (major == NULL && isdigit(*sp))
 1413         {
 1414             major = sp;
 1415         }
 1416         else if (!isdigit(*sp))
 1417         {
 1418             *sp = '\0';
 1419         }
 1420     }
 1421 
 1422     if (major != NULL)
 1423     {
 1424         snprintf(context, CF_BUFSIZE, "hpux_%s", major);
 1425         EvalContextClassPutHard(ctx, context, "source=agent,derived-from=sys.flavor");
 1426     }
 1427 #endif
 1428 
 1429 #ifdef __FreeBSD__
 1430     /*
 1431      * Define a hard class with just the version major number on FreeBSD
 1432      *
 1433      * For example, when being run on either FreeBSD 10.0 or 10.1 a class
 1434      * called freebsd_10 will be defined
 1435      */
 1436     for (char *sp = vbuff; *sp != '\0'; sp++)
 1437     {
 1438         if (*sp == '.')
 1439         {
 1440             *sp = '\0';
 1441             break;
 1442         }
 1443     }
 1444 
 1445     snprintf(context, CF_BUFSIZE, "%s_%s", VSYSNAME.sysname, vbuff);
 1446     EvalContextClassPutHard(ctx, context, "source=agent,derived-from=sys.flavor");
 1447 #endif
 1448 
 1449 #endif
 1450 
 1451 #ifdef XEN_CPUID_SUPPORT
 1452     if (Xen_Hv_Check())
 1453     {
 1454         Log(LOG_LEVEL_VERBOSE, "This appears to be a xen hv system.");
 1455         EvalContextClassPutHard(ctx, "xen", "inventory,attribute_name=Virtual host,source=agent");
 1456         EvalContextClassPutHard(ctx, "xen_domu_hv", "source=agent");
 1457     }
 1458 #endif /* XEN_CPUID_SUPPORT */
 1459 
 1460     GetCPUInfo(ctx);
 1461 
 1462 #ifdef __CYGWIN__
 1463 
 1464     for (char *sp = VSYSNAME.sysname; *sp != '\0'; sp++)
 1465     {
 1466         if (*sp == '-')
 1467         {
 1468             sp++;
 1469             if (strncmp(sp, "5.0", 3) == 0)
 1470             {
 1471                 Log(LOG_LEVEL_VERBOSE, "This appears to be Windows 2000");
 1472                 EvalContextClassPutHard(ctx, "Win2000", "inventory,attribute_name=none,source=agent");
 1473             }
 1474 
 1475             if (strncmp(sp, "5.1", 3) == 0)
 1476             {
 1477                 Log(LOG_LEVEL_VERBOSE, "This appears to be Windows XP");
 1478                 EvalContextClassPutHard(ctx, "WinXP", "inventory,attribute_name=none,source=agent");
 1479             }
 1480 
 1481             if (strncmp(sp, "5.2", 3) == 0)
 1482             {
 1483                 Log(LOG_LEVEL_VERBOSE, "This appears to be Windows Server 2003");
 1484                 EvalContextClassPutHard(ctx, "WinServer2003", "inventory,attribute_name=none,source=agent");
 1485             }
 1486 
 1487             if (strncmp(sp, "6.1", 3) == 0)
 1488             {
 1489                 Log(LOG_LEVEL_VERBOSE, "This appears to be Windows Vista");
 1490                 EvalContextClassPutHard(ctx, "WinVista", "inventory,attribute_name=none,source=agent");
 1491             }
 1492 
 1493             if (strncmp(sp, "6.3", 3) == 0)
 1494             {
 1495                 Log(LOG_LEVEL_VERBOSE, "This appears to be Windows Server 2008");
 1496                 EvalContextClassPutHard(ctx, "WinServer2008", "inventory,attribute_name=none,source=agent");
 1497             }
 1498         }
 1499     }
 1500 
 1501     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "crontab", "", CF_DATA_TYPE_STRING, "source=agent");
 1502 
 1503 #endif /* __CYGWIN__ */
 1504 
 1505 #ifdef __MINGW32__
 1506     EvalContextClassPutHard(ctx, VSYSNAME.release, "inventory,attribute_name=none,source=agent,derived-from=sys.release"); // code name - e.g. Windows Vista
 1507     EvalContextClassPutHard(ctx, VSYSNAME.version, "inventory,attribute_name=none,source=agent,derived-from=sys.version"); // service pack number - e.g. Service Pack 3
 1508 
 1509     if (strstr(VSYSNAME.sysname, "workstation"))
 1510     {
 1511         EvalContextClassPutHard(ctx, "WinWorkstation", "inventory,attribute_name=Windows roles,source=agent,derived-from=sys.sysname");
 1512     }
 1513     else if (strstr(VSYSNAME.sysname, "server"))
 1514     {
 1515         EvalContextClassPutHard(ctx, "WinServer", "inventory,attribute_name=Windows roles,source=agent,derived-from=sys.sysname");
 1516     }
 1517     else if (strstr(VSYSNAME.sysname, "domain controller"))
 1518     {
 1519         EvalContextClassPutHard(ctx, "DomainController", "inventory,attribute_name=Windows roles,source=agent,derived-from=sys.sysname");
 1520         EvalContextClassPutHard(ctx, "WinServer", "inventory,attribute_name=Windows roles,source=agent,derived-from=sys.sysname");
 1521     }
 1522     else
 1523     {
 1524         EvalContextClassPutHard(ctx, "unknown_ostype", "source=agent,derived-from=sys.sysname");
 1525     }
 1526 
 1527     SetFlavor(ctx, "windows");
 1528 
 1529 #endif /* __MINGW32__ */
 1530 
 1531 #ifndef _WIN32
 1532     struct passwd *pw;
 1533     if ((pw = getpwuid(getuid())) == NULL)
 1534     {
 1535         Log(LOG_LEVEL_ERR, "Unable to get username for uid '%ju'. (getpwuid: %s)", (uintmax_t)getuid(), GetErrorStr());
 1536     }
 1537     else
 1538     {
 1539         char vbuff[CF_BUFSIZE];
 1540 
 1541         if (EvalContextClassGet(ctx, NULL, "SUSE"))
 1542         {
 1543             snprintf(vbuff, CF_BUFSIZE, "/var/spool/cron/tabs/%s", pw->pw_name);
 1544         }
 1545         else if (EvalContextClassGet(ctx, NULL, "redhat"))
 1546         {
 1547             snprintf(vbuff, CF_BUFSIZE, "/var/spool/cron/%s", pw->pw_name);
 1548         }
 1549         else if (EvalContextClassGet(ctx, NULL, "freebsd"))
 1550         {
 1551             snprintf(vbuff, CF_BUFSIZE, "/var/cron/tabs/%s", pw->pw_name);
 1552         }
 1553         else
 1554         {
 1555             snprintf(vbuff, CF_BUFSIZE, "/var/spool/cron/crontabs/%s", pw->pw_name);
 1556         }
 1557 
 1558         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "crontab", vbuff, CF_DATA_TYPE_STRING, "source=agent");
 1559     }
 1560 
 1561 #endif
 1562 
 1563 #if defined(__ANDROID__)
 1564     SetFlavor(ctx, "android");
 1565 #endif
 1566 
 1567 #ifdef __sun
 1568     if (StringMatchFull("joyent.*", VSYSNAME.version))
 1569     {
 1570         EvalContextClassPutHard(ctx, "smartos", "inventory,attribute_name=none,source=agent,derived-from=sys.version");
 1571         EvalContextClassPutHard(ctx, "smartmachine", "source=agent,derived-from=sys.version");
 1572     }
 1573 #endif
 1574 
 1575     /* FIXME: this variable needs redhat/SUSE/debian classes to be defined and
 1576      * hence can't be initialized earlier */
 1577 
 1578     if (EvalContextClassGet(ctx, NULL, "redhat"))
 1579     {
 1580         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "doc_root", "/var/www/html", CF_DATA_TYPE_STRING, "source=agent");
 1581     }
 1582 
 1583     if (EvalContextClassGet(ctx, NULL, "SUSE"))
 1584     {
 1585         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "doc_root", "/srv/www/htdocs", CF_DATA_TYPE_STRING, "source=agent");
 1586     }
 1587 
 1588     if (EvalContextClassGet(ctx, NULL, "debian"))
 1589     {
 1590         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "doc_root", "/var/www", CF_DATA_TYPE_STRING, "source=agent");
 1591     }
 1592 }
 1593 
 1594 /*********************************************************************************/
 1595 
 1596 #ifdef __linux__
 1597 static void Linux_Oracle_VM_Server_Version(EvalContext *ctx)
 1598 {
 1599     char relstring[CF_MAXVARSIZE];
 1600     char *r;
 1601     int major, minor, patch;
 1602     int revcomps;
 1603 
 1604 #define ORACLE_VM_SERVER_REL_FILENAME "/etc/ovs-release"
 1605 #define ORACLE_VM_SERVER_ID "Oracle VM server"
 1606 
 1607     Log(LOG_LEVEL_VERBOSE, "This appears to be Oracle VM Server");
 1608     EvalContextClassPutHard(ctx, "redhat", "inventory,attribute_name=none,source=agent");
 1609     EvalContextClassPutHard(ctx, "oraclevmserver", "inventory,attribute_name=Virtual host,source=agent");
 1610 
 1611     if (!ReadLine(ORACLE_VM_SERVER_REL_FILENAME, relstring, sizeof(relstring)))
 1612     {
 1613         return;
 1614     }
 1615 
 1616     if (strncmp(relstring, ORACLE_VM_SERVER_ID, strlen(ORACLE_VM_SERVER_ID)))
 1617     {
 1618         Log(LOG_LEVEL_VERBOSE, "Could not identify distribution from %s", ORACLE_VM_SERVER_REL_FILENAME);
 1619         return;
 1620     }
 1621 
 1622     if ((r = strstr(relstring, "release ")) == NULL)
 1623     {
 1624         Log(LOG_LEVEL_VERBOSE, "Could not find distribution version in %s", ORACLE_VM_SERVER_REL_FILENAME);
 1625         return;
 1626     }
 1627 
 1628     revcomps = sscanf(r + strlen("release "), "%d.%d.%d", &major, &minor, &patch);
 1629 
 1630     if (revcomps > 0)
 1631     {
 1632         char buf[CF_BUFSIZE];
 1633 
 1634         snprintf(buf, CF_BUFSIZE, "oraclevmserver_%d", major);
 1635         SetFlavor(ctx, buf);
 1636     }
 1637 
 1638     if (revcomps > 1)
 1639     {
 1640         char buf[CF_BUFSIZE];
 1641 
 1642         snprintf(buf, CF_BUFSIZE, "oraclevmserver_%d_%d", major, minor);
 1643         EvalContextClassPutHard(ctx, buf, "inventory,attribute_name=none,source=agent");
 1644     }
 1645 
 1646     if (revcomps > 2)
 1647     {
 1648         char buf[CF_BUFSIZE];
 1649 
 1650         snprintf(buf, CF_BUFSIZE, "oraclevmserver_%d_%d_%d", major, minor, patch);
 1651         EvalContextClassPutHard(ctx, buf, "inventory,attribute_name=none,source=agent");
 1652     }
 1653 }
 1654 
 1655 /*********************************************************************************/
 1656 
 1657 static void Linux_Oracle_Version(EvalContext *ctx)
 1658 {
 1659     char relstring[CF_MAXVARSIZE];
 1660     char *r;
 1661     int major, minor;
 1662 
 1663 #define ORACLE_REL_FILENAME "/etc/oracle-release"
 1664 #define ORACLE_ID "Oracle Linux Server"
 1665 
 1666     Log(LOG_LEVEL_VERBOSE, "This appears to be Oracle Linux");
 1667     EvalContextClassPutHard(ctx, "oracle", "inventory,attribute_name=none,source=agent");
 1668 
 1669     if (!ReadLine(ORACLE_REL_FILENAME, relstring, sizeof(relstring)))
 1670     {
 1671         return;
 1672     }
 1673 
 1674     if (strncmp(relstring, ORACLE_ID, strlen(ORACLE_ID)))
 1675     {
 1676         Log(LOG_LEVEL_VERBOSE, "Could not identify distribution from %s", ORACLE_REL_FILENAME);
 1677         return;
 1678     }
 1679 
 1680     if ((r = strstr(relstring, "release ")) == NULL)
 1681     {
 1682         Log(LOG_LEVEL_VERBOSE, "Could not find distribution version in %s", ORACLE_REL_FILENAME);
 1683         return;
 1684     }
 1685 
 1686     if (sscanf(r + strlen("release "), "%d.%d", &major, &minor) == 2)
 1687     {
 1688         char buf[CF_BUFSIZE];
 1689 
 1690         snprintf(buf, CF_BUFSIZE, "oracle_%d", major);
 1691         SetFlavor(ctx, buf);
 1692 
 1693         snprintf(buf, CF_BUFSIZE, "oracle_%d_%d", major, minor);
 1694         EvalContextClassPutHard(ctx, buf, "inventory,attribute_name=none,source=agent");
 1695     }
 1696 }
 1697 
 1698 /*********************************************************************************/
 1699 
 1700 static int Linux_Fedora_Version(EvalContext *ctx)
 1701 {
 1702 #define FEDORA_ID "Fedora"
 1703 #define RELEASE_FLAG "release "
 1704 
 1705 /* We are looking for one of the following strings...
 1706  *
 1707  * Fedora Core release 1 (Yarrow)
 1708  * Fedora release 7 (Zodfoobar)
 1709  */
 1710 
 1711 #define FEDORA_REL_FILENAME "/etc/fedora-release"
 1712 
 1713 /* The full string read in from fedora-release */
 1714     char relstring[CF_MAXVARSIZE];
 1715 
 1716     Log(LOG_LEVEL_VERBOSE, "This appears to be a fedora system.");
 1717     EvalContextClassPutHard(ctx, "redhat", "inventory,attribute_name=none,source=agent");
 1718     EvalContextClassPutHard(ctx, "fedora", "inventory,attribute_name=none,source=agent");
 1719 
 1720 /* Grab the first line from the file and then close it. */
 1721 
 1722     if (!ReadLine(FEDORA_REL_FILENAME, relstring, sizeof(relstring)))
 1723     {
 1724         return 1;
 1725     }
 1726 
 1727     Log(LOG_LEVEL_VERBOSE, "Looking for fedora core linux info...");
 1728 
 1729     char *vendor = "";
 1730     if (!strncmp(relstring, FEDORA_ID, strlen(FEDORA_ID)))
 1731     {
 1732         vendor = "fedora";
 1733     }
 1734     else
 1735     {
 1736         Log(LOG_LEVEL_VERBOSE, "Could not identify OS distro from %s", FEDORA_REL_FILENAME);
 1737         return 2;
 1738     }
 1739 
 1740 /* Now, grok the release.  We assume that all the strings will
 1741  * have the word 'release' before the numerical release.
 1742  */
 1743     int major = -1;
 1744     char strmajor[PRINTSIZE(major)];
 1745     char *release = strstr(relstring, RELEASE_FLAG);
 1746 
 1747     if (release == NULL)
 1748     {
 1749         Log(LOG_LEVEL_VERBOSE, "Could not find a numeric OS release in %s", FEDORA_REL_FILENAME);
 1750         return 2;
 1751     }
 1752     else
 1753     {
 1754         release += strlen(RELEASE_FLAG);
 1755 
 1756         strmajor[0] = '\0';
 1757         if (sscanf(release, "%d", &major) != 0)
 1758         {
 1759             xsnprintf(strmajor, sizeof(strmajor), "%d", major);
 1760         }
 1761     }
 1762 
 1763     if (major != -1 && vendor[0] != '\0')
 1764     {
 1765         char classbuf[CF_MAXVARSIZE];
 1766         classbuf[0] = '\0';
 1767         strcat(classbuf, vendor);
 1768         EvalContextClassPutHard(ctx,classbuf, "inventory,attribute_name=none,source=agent");
 1769         strcat(classbuf, "_");
 1770         strcat(classbuf, strmajor);
 1771         SetFlavor(ctx, classbuf);
 1772     }
 1773 
 1774     return 0;
 1775 }
 1776 
 1777 /*********************************************************************************/
 1778 
 1779 static int Linux_Redhat_Version(EvalContext *ctx)
 1780 {
 1781 #define REDHAT_ID "Red Hat Linux"
 1782 #define REDHAT_ENT_ID "Red Hat Enterprise Linux"
 1783 #define REDHAT_AS_ID "Red Hat Enterprise Linux AS"
 1784 #define REDHAT_AS21_ID "Red Hat Linux Advanced Server"
 1785 #define REDHAT_ES_ID "Red Hat Enterprise Linux ES"
 1786 #define REDHAT_WS_ID "Red Hat Enterprise Linux WS"
 1787 #define REDHAT_C_ID "Red Hat Enterprise Linux Client"
 1788 #define REDHAT_S_ID "Red Hat Enterprise Linux Server"
 1789 #define REDHAT_W_ID "Red Hat Enterprise Linux Workstation"
 1790 #define REDHAT_CN_ID "Red Hat Enterprise Linux ComputeNode"
 1791 #define MANDRAKE_ID "Linux Mandrake"
 1792 #define MANDRAKE_10_1_ID "Mandrakelinux"
 1793 #define WHITEBOX_ID "White Box Enterprise Linux"
 1794 #define CENTOS_ID "CentOS"
 1795 #define SCIENTIFIC_SL_ID "Scientific Linux SL"
 1796 #define SCIENTIFIC_SL6_ID "Scientific Linux"
 1797 #define SCIENTIFIC_CERN_ID "Scientific Linux CERN"
 1798 #define RELEASE_FLAG "release "
 1799 #define ORACLE_4_5_ID "Enterprise Linux Enterprise Linux Server"
 1800 
 1801 /* We are looking for one of the following strings...
 1802  *
 1803  * Red Hat Linux release 6.2 (Zoot)
 1804  * Red Hat Enterprise Linux release 8.0 (Ootpa)
 1805  * Red Hat Linux Advanced Server release 2.1AS (Pensacola)
 1806  * Red Hat Enterprise Linux AS release 3 (Taroon)
 1807  * Red Hat Enterprise Linux WS release 3 (Taroon)
 1808  * Red Hat Enterprise Linux Client release 5 (Tikanga)
 1809  * Red Hat Enterprise Linux Server release 5 (Tikanga)
 1810  * Linux Mandrake release 7.1 (helium)
 1811  * Red Hat Enterprise Linux ES release 2.1 (Panama)
 1812  * White Box Enterprise linux release 3.0 (Liberation)
 1813  * Scientific Linux SL Release 4.0 (Beryllium)
 1814  * CentOS release 4.0 (Final)
 1815  */
 1816 
 1817 #define RH_REL_FILENAME "/etc/redhat-release"
 1818 
 1819     Log(LOG_LEVEL_VERBOSE, "This appears to be a redhat (or redhat-based) system.");
 1820     EvalContextClassPutHard(ctx, "redhat", "inventory,attribute_name=none,source=agent,derived-from-file="RH_REL_FILENAME);
 1821 
 1822     /* Grab the first line from the file and then close it. */
 1823     char relstring[CF_MAXVARSIZE];
 1824     if (!ReadLine(RH_REL_FILENAME, relstring, sizeof(relstring)))
 1825     {
 1826         return 1;
 1827     }
 1828 
 1829     Log(LOG_LEVEL_VERBOSE, "Looking for redhat linux info in '%s'", relstring);
 1830 
 1831     /* First, try to grok the vendor and edition (if any) */
 1832     char *edition = ""; /* as (Advanced Server, Enterprise) */
 1833     char *vendor = ""; /* Red Hat, Mandrake */
 1834     if (!strncmp(relstring, REDHAT_ES_ID, strlen(REDHAT_ES_ID)))
 1835     {
 1836         vendor = "redhat";
 1837         edition = "es";
 1838     }
 1839     else if (!strncmp(relstring, REDHAT_WS_ID, strlen(REDHAT_WS_ID)))
 1840     {
 1841         vendor = "redhat";
 1842         edition = "ws";
 1843     }
 1844     else if (!strncmp(relstring, REDHAT_WS_ID, strlen(REDHAT_WS_ID)))
 1845     {
 1846         vendor = "redhat";
 1847         edition = "ws";
 1848     }
 1849     else if (!strncmp(relstring, REDHAT_AS_ID, strlen(REDHAT_AS_ID)) ||
 1850              !strncmp(relstring, REDHAT_AS21_ID, strlen(REDHAT_AS21_ID)))
 1851     {
 1852         vendor = "redhat";
 1853         edition = "as";
 1854     }
 1855     else if (!strncmp(relstring, REDHAT_S_ID, strlen(REDHAT_S_ID)))
 1856     {
 1857         vendor = "redhat";
 1858         edition = "s";
 1859     }
 1860     else if (!strncmp(relstring, REDHAT_C_ID, strlen(REDHAT_C_ID))
 1861              || !strncmp(relstring, REDHAT_W_ID, strlen(REDHAT_W_ID)))
 1862     {
 1863         vendor = "redhat";
 1864         edition = "c";
 1865     }
 1866     else if (!strncmp(relstring, REDHAT_CN_ID, strlen(REDHAT_CN_ID)))
 1867     {
 1868         vendor = "redhat";
 1869         edition = "cn";
 1870     }
 1871     else if (!strncmp(relstring, REDHAT_ID, strlen(REDHAT_ID)))
 1872     {
 1873         vendor = "redhat";
 1874     }
 1875     else if (!strncmp(relstring, REDHAT_ENT_ID, strlen(REDHAT_ENT_ID)))
 1876     {
 1877         vendor = "redhat";
 1878     }
 1879     else if (!strncmp(relstring, MANDRAKE_ID, strlen(MANDRAKE_ID)))
 1880     {
 1881         vendor = "mandrake";
 1882     }
 1883     else if (!strncmp(relstring, MANDRAKE_10_1_ID, strlen(MANDRAKE_10_1_ID)))
 1884     {
 1885         vendor = "mandrake";
 1886     }
 1887     else if (!strncmp(relstring, WHITEBOX_ID, strlen(WHITEBOX_ID)))
 1888     {
 1889         vendor = "whitebox";
 1890     }
 1891     else if (!strncmp(relstring, SCIENTIFIC_SL_ID, strlen(SCIENTIFIC_SL_ID)))
 1892     {
 1893         vendor = "scientific";
 1894         edition = "sl";
 1895     }
 1896     else if (!strncmp(relstring, SCIENTIFIC_CERN_ID, strlen(SCIENTIFIC_CERN_ID)))
 1897     {
 1898         vendor = "scientific";
 1899         edition = "cern";
 1900     }
 1901     else if (!strncmp(relstring, SCIENTIFIC_SL6_ID, strlen(SCIENTIFIC_SL6_ID)))
 1902     {
 1903         vendor = "scientific";
 1904         edition = "sl";
 1905     }
 1906     else if (!strncmp(relstring, CENTOS_ID, strlen(CENTOS_ID)))
 1907     {
 1908         vendor = "centos";
 1909     }
 1910     else if (!strncmp(relstring, ORACLE_4_5_ID, strlen(ORACLE_4_5_ID)))
 1911     {
 1912         vendor = "oracle";
 1913         edition = "s";
 1914     }
 1915     else
 1916     {
 1917         Log(LOG_LEVEL_VERBOSE, "Could not identify OS distro from %s", RH_REL_FILENAME);
 1918         return 2;
 1919     }
 1920 
 1921 /* Now, grok the release.  For AS, we neglect the AS at the end of the
 1922  * numerical release because we already figured out that it *is* AS
 1923  * from the information above.  We assume that all the strings will
 1924  * have the word 'release' before the numerical release.
 1925  */
 1926 
 1927 /* Convert relstring to lowercase so that vendors like
 1928    Scientific Linux don't fall through the cracks.
 1929    */
 1930 
 1931     for (int i = 0; i < strlen(relstring); i++)
 1932     {
 1933         relstring[i] = tolower(relstring[i]);
 1934     }
 1935 
 1936     /* Where the numerical release will be found */
 1937     int major = -1, minor = -1;
 1938     char strmajor[PRINTSIZE(major)], strminor[PRINTSIZE(minor)];
 1939 
 1940     char *release = strstr(relstring, RELEASE_FLAG);
 1941     if (release == NULL)
 1942     {
 1943         Log(LOG_LEVEL_VERBOSE, "Could not find a numeric OS release in %s", RH_REL_FILENAME);
 1944         return 2;
 1945     }
 1946     else
 1947     {
 1948         release += strlen(RELEASE_FLAG);
 1949         if (sscanf(release, "%d.%d", &major, &minor) == 2)
 1950         {
 1951             xsnprintf(strmajor, sizeof(strmajor), "%d", major);
 1952             xsnprintf(strminor, sizeof(strminor), "%d", minor);
 1953         }
 1954         /* red hat 9 is *not* red hat 9.0.
 1955          * and same thing with RHEL AS 3
 1956          */
 1957         else if (sscanf(release, "%d", &major) == 1)
 1958         {
 1959             xsnprintf(strmajor, sizeof(strmajor), "%d", major);
 1960             minor = -2;
 1961         }
 1962     }
 1963 
 1964     char classbuf[CF_MAXVARSIZE];
 1965     if (major != -1 && minor != -1 && (strcmp(vendor, "") != 0))
 1966     {
 1967         classbuf[0] = '\0';
 1968         strcat(classbuf, vendor);
 1969         EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent,derived-from-file="RH_REL_FILENAME);
 1970         strcat(classbuf, "_");
 1971 
 1972         if (strcmp(edition, "") != 0)
 1973         {
 1974             strcat(classbuf, edition);
 1975             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent,derived-from-file="RH_REL_FILENAME);
 1976             strcat(classbuf, "_");
 1977         }
 1978 
 1979         strcat(classbuf, strmajor);
 1980         EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent,derived-from-file="RH_REL_FILENAME);
 1981 
 1982         if (minor != -2)
 1983         {
 1984             strcat(classbuf, "_");
 1985             strcat(classbuf, strminor);
 1986             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent,derived-from-file="RH_REL_FILENAME);
 1987         }
 1988     }
 1989 
 1990     // Now a version without the edition
 1991     if (major != -1 && minor != -1 && vendor[0] != '\0')
 1992     {
 1993         classbuf[0] = '\0';
 1994         strcat(classbuf, vendor);
 1995         EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent,derived-from-file="RH_REL_FILENAME);
 1996         strcat(classbuf, "_");
 1997 
 1998         strcat(classbuf, strmajor);
 1999 
 2000         SetFlavor(ctx, classbuf);
 2001 
 2002         if (minor != -2)
 2003         {
 2004             strcat(classbuf, "_");
 2005             strcat(classbuf, strminor);
 2006             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent,derived-from-file="RH_REL_FILENAME);
 2007         }
 2008     }
 2009 
 2010     return 0;
 2011 }
 2012 
 2013 /******************************************************************/
 2014 
 2015 static int Linux_Suse_Version(EvalContext *ctx)
 2016 {
 2017 #define SUSE_REL_FILENAME "/etc/SuSE-release"
 2018 /* Check if it's a SUSE Enterprise version (all in lowercase) */
 2019 #define SUSE_SLES8_ID "suse sles-8"
 2020 #define SUSE_SLES_ID  "suse linux enterprise server"
 2021 #define SUSE_SLED_ID  "suse linux enterprise desktop"
 2022 #define SUSE_RELEASE_FLAG "linux "
 2023 
 2024     char classbuf[CF_MAXVARSIZE];
 2025 
 2026     Log(LOG_LEVEL_VERBOSE, "This appears to be a SUSE system.");
 2027     EvalContextClassPutHard(ctx, "SUSE", "inventory,attribute_name=none,source=agent");
 2028     EvalContextClassPutHard(ctx, "suse", "inventory,attribute_name=none,source=agent");
 2029 
 2030     /* The correct spelling for SUSE is "SUSE" but CFEngine used to use "SuSE".
 2031      * Keep this for backwards compatibility until CFEngine 3.7
 2032      */
 2033     EvalContextClassPutHard(ctx, "SuSE", "inventory,attribute_name=none,source=agent");
 2034 
 2035     /* Grab the first line from the SuSE-release file and then close it. */
 2036     char relstring[CF_MAXVARSIZE];
 2037 
 2038     FILE *fp = ReadFirstLine(SUSE_REL_FILENAME, relstring, sizeof(relstring));
 2039     if (fp == NULL)
 2040     {
 2041         return 1;
 2042     }
 2043 
 2044     char vbuf[CF_BUFSIZE], strversion[CF_MAXVARSIZE], strpatch[CF_MAXVARSIZE];
 2045     strversion[0] = '\0';
 2046     strpatch[0] = '\0';
 2047 
 2048     int major = -1, minor = -1;
 2049     while (fgets(vbuf, sizeof(vbuf), fp) != NULL)
 2050     {
 2051         if (strncmp(vbuf, "VERSION", strlen("version")) == 0)
 2052         {
 2053             strlcpy(strversion, vbuf, sizeof(strversion));
 2054             sscanf(vbuf, "VERSION = %d", &major);
 2055         }
 2056 
 2057         if (strncmp(vbuf, "PATCH", strlen("PATCH")) == 0)
 2058         {
 2059             strlcpy(strpatch, vbuf, sizeof(strpatch));
 2060             sscanf(vbuf, "PATCHLEVEL = %d", &minor);
 2061         }
 2062     }
 2063     if (ferror(fp))
 2064     {
 2065         UnexpectedError("Failed to read line from stream");
 2066     }
 2067     else
 2068     {
 2069         assert(feof(fp));
 2070     }
 2071 
 2072     fclose(fp);
 2073 
 2074     /* Check if it's a SUSE Enterprise version  */
 2075 
 2076     Log(LOG_LEVEL_VERBOSE, "Looking for SUSE enterprise info in '%s'", relstring);
 2077 
 2078     /* Convert relstring to lowercase to handle rename of SuSE to
 2079      * SUSE with SUSE 10.0.
 2080      */
 2081 
 2082     for (int i = 0; i < strlen(relstring); i++)
 2083     {
 2084         relstring[i] = tolower(relstring[i]);
 2085     }
 2086 
 2087     /* Check if it's a SUSE Enterprise version (all in lowercase) */
 2088 
 2089     if (!strncmp(relstring, SUSE_SLES8_ID, strlen(SUSE_SLES8_ID)))
 2090     {
 2091         classbuf[0] = '\0';
 2092         strcat(classbuf, "SLES8");
 2093         EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2094     }
 2095     else if (strncmp(relstring, "sles", 4) == 0)
 2096     {
 2097         Item *list, *ip;
 2098 
 2099         sscanf(relstring, "%[-_a-zA-Z0-9]", vbuf);
 2100         EvalContextClassPutHard(ctx, vbuf, "inventory,attribute_name=none,source=agent");
 2101 
 2102         list = SplitString(vbuf, '-');
 2103 
 2104         for (ip = list; ip != NULL; ip = ip->next)
 2105         {
 2106             EvalContextClassPutHard(ctx, ip->name, "inventory,attribute_name=none,source=agent");
 2107         }
 2108 
 2109         DeleteItemList(list);
 2110     }
 2111     else
 2112     {
 2113         for (int version = 9; version < 13; version++)
 2114         {
 2115             snprintf(vbuf, CF_BUFSIZE, "%s %d ", SUSE_SLES_ID, version);
 2116             Log(LOG_LEVEL_DEBUG, "Checking for SUSE [%s]", vbuf);
 2117 
 2118             if (!strncmp(relstring, vbuf, strlen(vbuf)))
 2119             {
 2120                 snprintf(classbuf, CF_MAXVARSIZE, "SLES%d", version);
 2121                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2122             }
 2123             else
 2124             {
 2125                 snprintf(vbuf, CF_BUFSIZE, "%s %d ", SUSE_SLED_ID, version);
 2126                 Log(LOG_LEVEL_DEBUG, "Checking for SUSE [%s]", vbuf);
 2127 
 2128                 if (!strncmp(relstring, vbuf, strlen(vbuf)))
 2129                 {
 2130                     snprintf(classbuf, CF_MAXVARSIZE, "SLED%d", version);
 2131                     EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2132                 }
 2133             }
 2134         }
 2135     }
 2136 
 2137     /* Determine release version. We assume that the version follows
 2138      * the string "SuSE Linux" or "SUSE LINUX".
 2139      */
 2140 
 2141     char *release = strstr(relstring, SUSE_RELEASE_FLAG);
 2142     if (release == NULL)
 2143     {
 2144         release = strstr(relstring, "opensuse");
 2145         if (release == NULL)
 2146         {
 2147             release = strversion;
 2148         }
 2149     }
 2150 
 2151     if (release == NULL)
 2152     {
 2153         Log(LOG_LEVEL_VERBOSE,
 2154             "Could not find a numeric OS release in %s",
 2155             SUSE_REL_FILENAME);
 2156         return 2;
 2157     }
 2158     else
 2159     {
 2160         char strmajor[PRINTSIZE(major)], strminor[PRINTSIZE(minor)];
 2161         if (strchr(release, '.'))
 2162         {
 2163             sscanf(release, "%*s %d.%d", &major, &minor);
 2164             xsnprintf(strmajor, sizeof(strmajor), "%d", major);
 2165             xsnprintf(strminor, sizeof(strminor), "%d", minor);
 2166 
 2167             if (major != -1 && minor != -1)
 2168             {
 2169                 strcpy(classbuf, "SUSE");
 2170                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2171                 strcat(classbuf, "_");
 2172                 strcat(classbuf, strmajor);
 2173                 SetFlavor(ctx, classbuf);
 2174                 strcat(classbuf, "_");
 2175                 strcat(classbuf, strminor);
 2176                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2177 
 2178                 /* The correct spelling for SUSE is "SUSE" but CFEngine used to use "SuSE".
 2179                  * Keep this for backwards compatibility until CFEngine 3.7
 2180                  */
 2181                 strcpy(classbuf, "SuSE");
 2182                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2183                 strcat(classbuf, "_");
 2184                 strcat(classbuf, strmajor);
 2185                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2186                 strcat(classbuf, "_");
 2187                 strcat(classbuf, strminor);
 2188                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2189 
 2190                 Log(LOG_LEVEL_VERBOSE, "Discovered SUSE version %s", classbuf);
 2191                 return 0;
 2192             }
 2193         }
 2194         else
 2195         {
 2196             sscanf(strversion, "VERSION = %s", strmajor);
 2197             sscanf(strpatch, "PATCHLEVEL = %s", strminor);
 2198 
 2199             if (major != -1 && minor != -1)
 2200             {
 2201                 strcpy(classbuf, "SLES");
 2202                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2203                 strcat(classbuf, "_");
 2204                 strcat(classbuf, strmajor);
 2205                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2206                 strcat(classbuf, "_");
 2207                 strcat(classbuf, strminor);
 2208                 EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2209 
 2210                 snprintf(classbuf, CF_MAXVARSIZE, "SUSE_%d", major);
 2211                 SetFlavor(ctx, classbuf);
 2212 
 2213                 /* The correct spelling for SUSE is "SUSE" but CFEngine used to use "SuSE".
 2214                  * Keep this for backwards compatibility until CFEngine 3.7
 2215                  */
 2216                 snprintf(classbuf, CF_MAXVARSIZE, "SuSE_%d", major);
 2217                 EvalContextClassPutHard(ctx, classbuf, "source=agent");
 2218 
 2219                 Log(LOG_LEVEL_VERBOSE, "Discovered SUSE version %s", classbuf);
 2220                 return 0;
 2221             }
 2222         }
 2223     }
 2224 
 2225     Log(LOG_LEVEL_VERBOSE, "Could not find a numeric OS release in %s", SUSE_REL_FILENAME);
 2226 
 2227     return 0;
 2228 }
 2229 
 2230 /******************************************************************/
 2231 
 2232 static int Linux_Slackware_Version(EvalContext *ctx, char *filename)
 2233 {
 2234     int major = -1;
 2235     int minor = -1;
 2236     int release = -1;
 2237     char classname[CF_MAXVARSIZE] = "";
 2238     char buffer[CF_MAXVARSIZE];
 2239 
 2240     Log(LOG_LEVEL_VERBOSE, "This appears to be a slackware system.");
 2241     EvalContextClassPutHard(ctx, "slackware", "inventory,attribute_name=none,source=agent");
 2242 
 2243     if (!ReadLine(filename, buffer, sizeof(buffer)))
 2244     {
 2245         return 1;
 2246     }
 2247 
 2248     Log(LOG_LEVEL_VERBOSE, "Looking for Slackware version...");
 2249     switch (sscanf(buffer, "Slackware %d.%d.%d", &major, &minor, &release))
 2250     {
 2251     case 3:
 2252         Log(LOG_LEVEL_VERBOSE, "This appears to be a Slackware %u.%u.%u system.", major, minor, release);
 2253         snprintf(classname, CF_MAXVARSIZE, "slackware_%u_%u_%u", major, minor, release);
 2254         EvalContextClassPutHard(ctx, classname, "inventory,attribute_name=none,source=agent");
 2255         /* Fall-through */
 2256     case 2:
 2257         Log(LOG_LEVEL_VERBOSE, "This appears to be a Slackware %u.%u system.", major, minor);
 2258         snprintf(classname, CF_MAXVARSIZE, "slackware_%u_%u", major, minor);
 2259         EvalContextClassPutHard(ctx, classname, "inventory,attribute_name=none,source=agent");
 2260         /* Fall-through */
 2261     case 1:
 2262         Log(LOG_LEVEL_VERBOSE, "This appears to be a Slackware %u system.", major);
 2263         snprintf(classname, CF_MAXVARSIZE, "slackware_%u", major);
 2264         EvalContextClassPutHard(ctx, classname, "inventory,attribute_name=none,source=agent");
 2265         break;
 2266     case 0:
 2267         Log(LOG_LEVEL_VERBOSE, "No Slackware version number found.");
 2268         return 2;
 2269     }
 2270     return 0;
 2271 }
 2272 
 2273 /*
 2274  * @brief Purge /etc/issue escapes on debian
 2275  *
 2276  * On debian, /etc/issue can include special characters escaped with
 2277  * '\\' or '@'. This function removes such escape sequences.
 2278  *
 2279  * @param[in,out] buffer: string to be sanitized
 2280  */
 2281 static void LinuxDebianSanitizeIssue(char *buffer)
 2282 {
 2283     bool escaped = false;
 2284     char *dst = buffer, *src = buffer, *tail = dst;
 2285     while (*src != '\0')
 2286     {
 2287         char here = *src;
 2288         src++;
 2289         if (here == '\\' || here == '@' || escaped)
 2290         {
 2291             /* Skip over escapes and the character each acts on. */
 2292             escaped = !escaped;
 2293         }
 2294         else
 2295         {
 2296             /* Copy everything else verbatim: */
 2297             *dst = here;
 2298             dst++;
 2299             /* Keep track of (just after) last non-space: */
 2300             if (!isspace(here))
 2301             {
 2302                 tail = dst;
 2303             }
 2304         }
 2305     }
 2306 
 2307     assert(tail == dst || isspace(*tail));
 2308     *tail = '\0';
 2309 }
 2310 
 2311 /******************************************************************/
 2312 
 2313 static int Linux_Misc_Version(EvalContext *ctx)
 2314 {
 2315     char flavor[CF_MAXVARSIZE];
 2316     char version[CF_MAXVARSIZE];
 2317     char os[CF_MAXVARSIZE];
 2318     char buffer[CF_BUFSIZE];
 2319 
 2320     *os = '\0';
 2321     *version = '\0';
 2322 
 2323     FILE *fp = safe_fopen(LSB_RELEASE_FILENAME, "r");
 2324     if (fp != NULL)
 2325     {
 2326         while (!feof(fp))
 2327         {
 2328             if (fgets(buffer, CF_BUFSIZE, fp) == NULL)
 2329             {
 2330                 if (ferror(fp))
 2331                 {
 2332                     break;
 2333                 }
 2334                 continue;
 2335             }
 2336 
 2337             if (strstr(buffer, "Cumulus"))
 2338             {
 2339                 EvalContextClassPutHard(ctx, "cumulus", "inventory,attribute_name=none,source=agent");
 2340                 strcpy(os, "cumulus");
 2341             }
 2342 
 2343             char *sp = strstr(buffer, "DISTRIB_RELEASE=");
 2344             if (sp)
 2345             {
 2346                 version[0] = '\0';
 2347                 sscanf(sp + strlen("DISTRIB_RELEASE="), "%[^\n]", version);
 2348                 CanonifyNameInPlace(version);
 2349             }
 2350         }
 2351     fclose(fp);
 2352     }
 2353 
 2354     if (*os && *version)
 2355     {
 2356         snprintf(flavor, CF_MAXVARSIZE, "%s_%s", os, version);
 2357         SetFlavor(ctx, flavor);
 2358         return 1;
 2359     }
 2360 
 2361     return 0;
 2362 }
 2363 
 2364 /******************************************************************/
 2365 
 2366 static int Linux_Debian_Version(EvalContext *ctx)
 2367 {
 2368     int major = -1;
 2369     int release = -1;
 2370     int result;
 2371     char classname[CF_MAXVARSIZE], buffer[CF_MAXVARSIZE], os[CF_MAXVARSIZE], version[CF_MAXVARSIZE];
 2372 
 2373     Log(LOG_LEVEL_VERBOSE, "This appears to be a debian system.");
 2374     EvalContextClassPutHard(
 2375         ctx,
 2376         "debian",
 2377         "inventory,attribute_name=none,source=agent,derived-from-file="DEBIAN_VERSION_FILENAME);
 2378 
 2379     buffer[0] = classname[0] = '\0';
 2380 
 2381     Log(LOG_LEVEL_VERBOSE, "Looking for Debian version...");
 2382 
 2383     if (!ReadLine(DEBIAN_VERSION_FILENAME, buffer, sizeof(buffer)))
 2384     {
 2385         return 1;
 2386     }
 2387 
 2388     result = sscanf(buffer, "%d.%d", &major, &release);
 2389 
 2390     switch (result)
 2391     {
 2392     case 2:
 2393         Log(LOG_LEVEL_VERBOSE, "This appears to be a Debian %u.%u system.", major, release);
 2394         snprintf(classname, CF_MAXVARSIZE, "debian_%u_%u", major, release);
 2395         EvalContextClassPutHard(
 2396             ctx,
 2397             classname,
 2398             "inventory,attribute_name=none,source=agent,derived-from-file="DEBIAN_VERSION_FILENAME);
 2399         snprintf(classname, CF_MAXVARSIZE, "debian_%u", major);
 2400         SetFlavor(ctx, classname);
 2401         break;
 2402 
 2403     case 1:
 2404         Log(LOG_LEVEL_VERBOSE, "This appears to be a Debian %u system.", major);
 2405         snprintf(classname, CF_MAXVARSIZE, "debian_%u", major);
 2406         SetFlavor(ctx, classname);
 2407         break;
 2408 
 2409     default:
 2410         version[0] = '\0';
 2411         sscanf(buffer, "%25[^/]", version);
 2412         if (strlen(version) > 0)
 2413         {
 2414             snprintf(classname, CF_MAXVARSIZE, "debian_%s", version);
 2415             EvalContextClassPutHard(
 2416                 ctx,
 2417                 classname,
 2418                 "inventory,attribute_name=none,source=agent,derived-from-file="DEBIAN_VERSION_FILENAME);
 2419         }
 2420         break;
 2421     }
 2422 
 2423     if (!ReadLine(DEBIAN_ISSUE_FILENAME, buffer, sizeof(buffer)))
 2424     {
 2425         return 1;
 2426     }
 2427 
 2428     os[0] = '\0';
 2429     sscanf(buffer, "%250s", os);
 2430 
 2431     if (strcmp(os, "Debian") == 0)
 2432     {
 2433         LinuxDebianSanitizeIssue(buffer);
 2434         sscanf(buffer, "%*s %*s %[^./]", version);
 2435         snprintf(buffer, CF_MAXVARSIZE, "debian_%s", version);
 2436         EvalContextClassPutHard(
 2437             ctx,
 2438             "debian",
 2439             "inventory,attribute_name=none,source=agent,derived-from-file="DEBIAN_ISSUE_FILENAME);
 2440         SetFlavor(ctx, buffer);
 2441     }
 2442     else if (strcmp(os, "Ubuntu") == 0)
 2443     {
 2444         LinuxDebianSanitizeIssue(buffer);
 2445         char minor[CF_MAXVARSIZE] = {0};
 2446         sscanf(buffer, "%*s %[^.].%s", version, minor);
 2447         snprintf(buffer, CF_MAXVARSIZE, "ubuntu_%s", version);
 2448         SetFlavor(ctx, buffer);
 2449         EvalContextClassPutHard(
 2450             ctx,
 2451             "ubuntu",
 2452             "inventory,attribute_name=none,source=agent,derived-from-file="DEBIAN_ISSUE_FILENAME);
 2453         if (release >= 0)
 2454         {
 2455             snprintf(buffer, CF_MAXVARSIZE, "ubuntu_%s_%s", version, minor);
 2456             EvalContextClassPutHard(
 2457                 ctx,
 2458                 buffer,
 2459                 "inventory,attribute_name=none,source=agent,derived-from-file="DEBIAN_ISSUE_FILENAME);
 2460         }
 2461     }
 2462 
 2463     return 0;
 2464 }
 2465 
 2466 /******************************************************************/
 2467 
 2468 static int Linux_Mandrake_Version(EvalContext *ctx)
 2469 {
 2470 /* We are looking for one of the following strings... */
 2471 #define MANDRAKE_ID "Linux Mandrake"
 2472 #define MANDRAKE_REV_ID "Mandrake Linux"
 2473 #define MANDRAKE_10_1_ID "Mandrakelinux"
 2474 
 2475 #define MANDRAKE_REL_FILENAME "/etc/mandrake-release"
 2476 
 2477     char relstring[CF_MAXVARSIZE];
 2478     char *vendor = NULL;
 2479 
 2480     Log(LOG_LEVEL_VERBOSE, "This appears to be a mandrake system.");
 2481     EvalContextClassPutHard(ctx, "Mandrake", "inventory,attribute_name=none,source=agent");
 2482 
 2483     if (!ReadLine(MANDRAKE_REL_FILENAME, relstring, sizeof(relstring)))
 2484     {
 2485         return 1;
 2486     }
 2487 
 2488     Log(LOG_LEVEL_VERBOSE, "Looking for Mandrake linux info in '%s'", relstring);
 2489 
 2490 /* Older Mandrakes had the 'Mandrake Linux' string in reverse order */
 2491 
 2492     if (!strncmp(relstring, MANDRAKE_ID, strlen(MANDRAKE_ID)))
 2493     {
 2494         vendor = "mandrake";
 2495     }
 2496     else if (!strncmp(relstring, MANDRAKE_REV_ID, strlen(MANDRAKE_REV_ID)))
 2497     {
 2498         vendor = "mandrake";
 2499     }
 2500 
 2501     else if (!strncmp(relstring, MANDRAKE_10_1_ID, strlen(MANDRAKE_10_1_ID)))
 2502     {
 2503         vendor = "mandrake";
 2504     }
 2505     else
 2506     {
 2507         Log(LOG_LEVEL_VERBOSE, "Could not identify OS distro from %s", MANDRAKE_REL_FILENAME);
 2508         return 2;
 2509     }
 2510 
 2511     return Linux_Mandriva_Version_Real(ctx, MANDRAKE_REL_FILENAME, relstring, vendor);
 2512 }
 2513 
 2514 /******************************************************************/
 2515 
 2516 static int Linux_Mandriva_Version(EvalContext *ctx)
 2517 {
 2518 /* We are looking for the following strings... */
 2519 #define MANDRIVA_ID "Mandriva Linux"
 2520 
 2521 #define MANDRIVA_REL_FILENAME "/etc/mandriva-release"
 2522 
 2523     char relstring[CF_MAXVARSIZE];
 2524     char *vendor = NULL;
 2525 
 2526     Log(LOG_LEVEL_VERBOSE, "This appears to be a mandriva system.");
 2527     EvalContextClassPutHard(ctx, "Mandrake", "inventory,attribute_name=none,source=agent");
 2528     EvalContextClassPutHard(ctx, "Mandriva", "inventory,attribute_name=none,source=agent");
 2529 
 2530     if (!ReadLine(MANDRIVA_REL_FILENAME, relstring, sizeof(relstring)))
 2531     {
 2532         return 1;
 2533     }
 2534 
 2535     Log(LOG_LEVEL_VERBOSE, "Looking for Mandriva linux info in '%s'", relstring);
 2536 
 2537     if (!strncmp(relstring, MANDRIVA_ID, strlen(MANDRIVA_ID)))
 2538     {
 2539         vendor = "mandriva";
 2540     }
 2541     else
 2542     {
 2543         Log(LOG_LEVEL_VERBOSE, "Could not identify OS distro from '%s'", MANDRIVA_REL_FILENAME);
 2544         return 2;
 2545     }
 2546 
 2547     return Linux_Mandriva_Version_Real(ctx, MANDRIVA_REL_FILENAME, relstring, vendor);
 2548 
 2549 }
 2550 
 2551 /******************************************************************/
 2552 
 2553 static int Linux_Mandriva_Version_Real(EvalContext *ctx, char *filename, char *relstring, char *vendor)
 2554 {
 2555     int major = -1, minor = -1;
 2556     char strmajor[PRINTSIZE(major)], strminor[PRINTSIZE(minor)];
 2557 
 2558 #define RELEASE_FLAG "release "
 2559     char *release = strstr(relstring, RELEASE_FLAG);
 2560     if (release == NULL)
 2561     {
 2562         Log(LOG_LEVEL_VERBOSE, "Could not find a numeric OS release in %s", filename);
 2563         return 2;
 2564     }
 2565     else
 2566     {
 2567         release += strlen(RELEASE_FLAG);
 2568         if (sscanf(release, "%d.%d", &major, &minor) == 2)
 2569         {
 2570             xsnprintf(strmajor, sizeof(strmajor), "%d", major);
 2571             xsnprintf(strminor, sizeof(strminor), "%d", minor);
 2572         }
 2573         else
 2574         {
 2575             Log(LOG_LEVEL_VERBOSE, "Could not break down release version numbers in %s", filename);
 2576         }
 2577     }
 2578 
 2579     if (major != -1 && minor != -1 && strcmp(vendor, ""))
 2580     {
 2581         char classbuf[CF_MAXVARSIZE];
 2582         classbuf[0] = '\0';
 2583         strcat(classbuf, vendor);
 2584         EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2585         strcat(classbuf, "_");
 2586         strcat(classbuf, strmajor);
 2587         EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2588         if (minor != -2)
 2589         {
 2590             strcat(classbuf, "_");
 2591             strcat(classbuf, strminor);
 2592             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2593         }
 2594     }
 2595 
 2596     return 0;
 2597 }
 2598 
 2599 /******************************************************************/
 2600 
 2601 static void Linux_Amazon_Version(EvalContext *ctx)
 2602 {
 2603     char buffer[CF_BUFSIZE];
 2604 
 2605     // Amazon Linux AMI release 2016.09
 2606 
 2607     if (ReadLine("/etc/system-release", buffer, sizeof(buffer)))
 2608     {
 2609         if (strstr(buffer, "Amazon") != NULL)
 2610         {
 2611             EvalContextClassPutHard(ctx, "amazon_linux",
 2612                 "inventory,attribute_name=none,source=agent"
 2613                 ",derived-from-file=/etc/system-release");
 2614 
 2615             char version[128];
 2616             if (sscanf(buffer, "%*s %*s %*s %*s %127s", version) == 1)
 2617             {
 2618                 char class[CF_MAXVARSIZE];
 2619 
 2620                 CanonifyNameInPlace(version);
 2621                 snprintf(class, sizeof(class), "amazon_linux_%s", version);
 2622                 EvalContextClassPutHard(ctx, class,
 2623                     "inventory,attribute_name=none,source=agent"
 2624                     ",derived-from-file=/etc/system-release");
 2625             }
 2626             SetFlavor(ctx, "AmazonLinux");
 2627         }
 2628     }
 2629 }
 2630 
 2631 /******************************************************************/
 2632 
 2633 static void Linux_Alpine_Version(EvalContext *ctx)
 2634 {
 2635     char buffer[CF_BUFSIZE];
 2636 
 2637     Log(LOG_LEVEL_VERBOSE, "This appears to be an AlpineLinux system.");
 2638 
 2639     EvalContextClassPutHard(ctx, "alpine_linux",
 2640         "inventory,attribute_name=none,source=agent"
 2641         ",derived-from-file=/etc/alpine-release");
 2642 
 2643     if (ReadLine("/etc/alpine-release", buffer, sizeof(buffer)))
 2644     {
 2645         char version[128];
 2646         if (sscanf(buffer, "%127s", version) == 1)
 2647         {
 2648             char class[CF_MAXVARSIZE];
 2649             CanonifyNameInPlace(version);
 2650             snprintf(class, sizeof(class), "alpine_linux_%s", version);
 2651             EvalContextClassPutHard(ctx, class,
 2652                 "inventory,attribute_name=none,source=agent"
 2653                 ",derived-from-file=/etc/alpine-release");
 2654         }
 2655     }
 2656     SetFlavor(ctx, "alpinelinux");
 2657 }
 2658 
 2659 /******************************************************************/
 2660 
 2661 static int EOS_Version(EvalContext *ctx)
 2662 
 2663 {
 2664     char buffer[CF_BUFSIZE];
 2665 
 2666  // e.g. Arista Networks EOS 4.10.2
 2667 
 2668     if (ReadLine("/etc/Eos-release", buffer, sizeof(buffer)))
 2669     {
 2670         if (strstr(buffer, "EOS"))
 2671         {
 2672             char version[CF_MAXVARSIZE], class[CF_MAXVARSIZE];
 2673             EvalContextClassPutHard(ctx, "eos", "inventory,attribute_name=none,source=agent");
 2674             EvalContextClassPutHard(ctx, "arista", "source=agent");
 2675             version[0] = '\0';
 2676             sscanf(buffer, "%*s %*s %*s %s", version);
 2677             CanonifyNameInPlace(version);
 2678             snprintf(class, CF_MAXVARSIZE, "eos_%s", version);
 2679             EvalContextClassPutHard(ctx, class, "inventory,attribute_name=none,source=agent");
 2680         }
 2681     }
 2682 
 2683     return 0;
 2684 }
 2685 
 2686 /******************************************************************/
 2687 
 2688 static int MiscOS(EvalContext *ctx)
 2689 
 2690 { char buffer[CF_BUFSIZE];
 2691 
 2692  // e.g. BIG-IP 10.1.0 Build 3341.1084
 2693 
 2694     if (ReadLine("/etc/issue", buffer, sizeof(buffer)))
 2695     {
 2696        if (strstr(buffer, "BIG-IP"))
 2697        {
 2698            char version[CF_MAXVARSIZE], build[CF_MAXVARSIZE], class[CF_MAXVARSIZE];
 2699            EvalContextClassPutHard(ctx, "big_ip", "inventory,attribute_name=none,source=agent");
 2700            sscanf(buffer, "%*s %s %*s %s", version, build);
 2701            CanonifyNameInPlace(version);
 2702            CanonifyNameInPlace(build);
 2703            snprintf(class, CF_MAXVARSIZE, "big_ip_%s", version);
 2704            EvalContextClassPutHard(ctx, class, "inventory,attribute_name=none,source=agent");
 2705            snprintf(class, CF_MAXVARSIZE, "big_ip_%s_%s", version, build);
 2706            EvalContextClassPutHard(ctx, class, "inventory,attribute_name=none,source=agent");
 2707            SetFlavor(ctx, "BIG-IP");
 2708        }
 2709     }
 2710 
 2711     return 0;
 2712 }
 2713 
 2714 /******************************************************************/
 2715 
 2716 static int VM_Version(EvalContext *ctx)
 2717 {
 2718     char *sp, buffer[CF_BUFSIZE], classbuf[CF_BUFSIZE], version[CF_BUFSIZE];
 2719     int major, minor, bug;
 2720     int sufficient = 0;
 2721 
 2722     Log(LOG_LEVEL_VERBOSE, "This appears to be a VMware Server ESX/xSX system.");
 2723     EvalContextClassPutHard(ctx, "VMware", "inventory,attribute_name=Virtual host,source=agent");
 2724 
 2725 /* VMware Server ESX >= 3 has version info in /proc */
 2726     if (ReadLine("/proc/vmware/version", buffer, sizeof(buffer)))
 2727     {
 2728         if (sscanf(buffer, "VMware ESX Server %d.%d.%d", &major, &minor, &bug) > 0)
 2729         {
 2730             snprintf(classbuf, CF_BUFSIZE, "VMware ESX Server %d", major);
 2731             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2732             snprintf(classbuf, CF_BUFSIZE, "VMware ESX Server %d.%d", major, minor);
 2733             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2734             snprintf(classbuf, CF_BUFSIZE, "VMware ESX Server %d.%d.%d", major, minor, bug);
 2735             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2736             sufficient = 1;
 2737         }
 2738         else if (sscanf(buffer, "VMware ESX Server %s", version) > 0)
 2739         {
 2740             snprintf(classbuf, CF_BUFSIZE, "VMware ESX Server %s", version);
 2741             EvalContextClassPutHard(ctx, classbuf, "inventory,attribute_name=none,source=agent");
 2742             sufficient = 1;
 2743         }
 2744     }
 2745 
 2746 /* Fall back to checking for other files */
 2747 
 2748     if (sufficient < 1 && (ReadLine("/etc/vmware-release", buffer, sizeof(buffer))
 2749                            || ReadLine("/etc/issue", buffer, sizeof(buffer))))
 2750     {
 2751         EvalContextClassPutHard(ctx, buffer, "inventory,attribute_name=none,source=agent");
 2752 
 2753         /* Strip off the release code name e.g. "(Dali)" */
 2754         if ((sp = strchr(buffer, '(')) != NULL)
 2755         {
 2756             *sp = 0;
 2757             Chop(buffer, CF_EXPANDSIZE);
 2758             EvalContextClassPutHard(ctx, buffer, "inventory,attribute_name=none,source=agent");
 2759         }
 2760         sufficient = 1;
 2761     }
 2762 
 2763     return sufficient < 1 ? 1 : 0;
 2764 }
 2765 
 2766 /******************************************************************/
 2767 
 2768 static int Xen_Domain(EvalContext *ctx)
 2769 {
 2770     int sufficient = 0;
 2771 
 2772     Log(LOG_LEVEL_VERBOSE, "This appears to be a xen pv system.");
 2773     EvalContextClassPutHard(ctx, "xen", "inventory,attribute_name=Virtual host,source=agent");
 2774 
 2775 /* xen host will have "control_d" in /proc/xen/capabilities, xen guest will not */
 2776 
 2777     FILE *fp = safe_fopen("/proc/xen/capabilities", "r");
 2778     if (fp != NULL)
 2779     {
 2780         size_t buffer_size = CF_BUFSIZE;
 2781         char *buffer = xmalloc(buffer_size);
 2782 
 2783         for (;;)
 2784         {
 2785             ssize_t res = CfReadLine(&buffer, &buffer_size, fp);
 2786             if (res == -1)
 2787             {
 2788                 if (!feof(fp))
 2789                 {
 2790                     /* Failure reading Xen capabilites. Do we care? */
 2791                     fclose(fp);
 2792                     free(buffer);
 2793                     return 1;
 2794                 }
 2795                 else
 2796                 {
 2797                     break;
 2798                 }
 2799             }
 2800 
 2801             if (strstr(buffer, "control_d"))
 2802             {
 2803                 EvalContextClassPutHard(ctx, "xen_dom0", "inventory,attribute_name=Virtual host,source=agent");
 2804                 sufficient = 1;
 2805             }
 2806         }
 2807 
 2808         if (!sufficient)
 2809         {
 2810             EvalContextClassPutHard(ctx, "xen_domu_pv", "inventory,attribute_name=Virtual host,source=agent");
 2811             sufficient = 1;
 2812         }
 2813 
 2814         fclose(fp);
 2815         free(buffer);
 2816     }
 2817 
 2818     return sufficient < 1 ? 1 : 0;
 2819 }
 2820 
 2821 /******************************************************************/
 2822 static void OpenVZ_Detect(EvalContext *ctx)
 2823 {
 2824 /* paths to file defining the type of vm (guest or host) */
 2825 #define OPENVZ_HOST_FILENAME "/proc/bc/0"
 2826 #define OPENVZ_GUEST_FILENAME "/proc/vz"
 2827 /* path to the vzps binary */
 2828 #define OPENVZ_VZPS_FILE "/bin/vzps"
 2829     struct stat statbuf;
 2830 
 2831     /* The file /proc/bc/0 is present on host
 2832        The file /proc/vz is present on guest
 2833        If the host has /bin/vzps, we should use it for checking processes
 2834     */
 2835 
 2836     if (stat(OPENVZ_HOST_FILENAME, &statbuf) != -1)
 2837     {
 2838         Log(LOG_LEVEL_VERBOSE, "This appears to be an OpenVZ/Virtuozzo/Parallels Cloud Server host system.\n");
 2839         EvalContextClassPutHard(ctx, "virt_host_vz", "inventory,attribute_name=Virtual host,source=agent");
 2840         /* if the file /bin/vzps is there, it is safe to use the processes promise type */
 2841         if (stat(OPENVZ_VZPS_FILE, &statbuf) != -1)
 2842         {
 2843             EvalContextClassPutHard(ctx, "virt_host_vz_vzps", "inventory,attribute_name=Virtual host,source=agent");
 2844             /* here we must redefine the value of VPSHARDCLASS */
 2845             for (int i = 0; i < PLATFORM_CONTEXT_MAX; i++)
 2846             {
 2847                 if (!strcmp(CLASSATTRIBUTES[i][0], "virt_host_vz_vzps"))
 2848                 {
 2849                    VPSHARDCLASS = (PlatformContext) i;
 2850                    break;
 2851                 }
 2852             }
 2853         }
 2854         else
 2855         {
 2856             Log(LOG_LEVEL_NOTICE, "This OpenVZ/Virtuozzo/Parallels Cloud Server host system does not have vzps installed; the processes promise type may not work as expected.\n");
 2857         }
 2858     }
 2859     else if (stat(OPENVZ_GUEST_FILENAME, &statbuf) != -1)
 2860     {
 2861         Log(LOG_LEVEL_VERBOSE, "This appears to be an OpenVZ/Virtuozzo/Parallels Cloud Server guest system.\n");
 2862         EvalContextClassPutHard(ctx, "virt_guest_vz", "inventory,attribute_name=Virtual host,source=agent");
 2863     }
 2864 }
 2865 
 2866 /******************************************************************/
 2867 
 2868 static bool ReadLine(const char *filename, char *buf, int bufsize)
 2869 {
 2870     FILE *fp = ReadFirstLine(filename, buf, bufsize);
 2871 
 2872     if (fp == NULL)
 2873     {
 2874         return false;
 2875     }
 2876     else
 2877     {
 2878         fclose(fp);
 2879         return true;
 2880     }
 2881 }
 2882 
 2883 static FILE *ReadFirstLine(const char *filename, char *buf, int bufsize)
 2884 {
 2885     FILE *fp = safe_fopen(filename, "r");
 2886 
 2887     if (fp == NULL)
 2888     {
 2889         return NULL;
 2890     }
 2891 
 2892     if (fgets(buf, bufsize, fp) == NULL)
 2893     {
 2894         fclose(fp);
 2895         return NULL;
 2896     }
 2897 
 2898     StripTrailingNewline(buf, CF_EXPANDSIZE);
 2899 
 2900     return fp;
 2901 }
 2902 #endif /* __linux__ */
 2903 
 2904 /******************************************************************/
 2905 
 2906 #ifdef XEN_CPUID_SUPPORT
 2907 
 2908 /* Borrowed and modified from Xen source/tools/libxc/xc_cpuid_x86.c */
 2909 
 2910 static void Xen_Cpuid(uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
 2911 {
 2912 # ifdef __i386__
 2913     /* On i386, %ebx register needs to be saved before usage and restored
 2914      * thereafter for PIC-compliant code on i386. */
 2915     asm("push %%ebx; cpuid; mov %%ebx,%1; pop %%ebx"
 2916         : "=a"(*eax), "=r"(*ebx), "=c"(*ecx), "=d"(*edx)
 2917         : "0" (idx),  "2" (0) );
 2918 # else
 2919     asm("cpuid"
 2920         : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
 2921         : "0" (idx),  "2" (0) );
 2922 # endif
 2923 }
 2924 
 2925 /******************************************************************/
 2926 
 2927 static bool Xen_Hv_Check(void)
 2928 {
 2929     /* CPUID interface to Xen from arch-x86/cpuid.h:
 2930      * Leaf 1 (0x40000000)
 2931      * EAX: Largest Xen-information leaf. All leaves up to an including @EAX
 2932      *   are supported by the Xen host.
 2933      * EBX-EDX: "XenVMMXenVMM" signature, allowing positive identification
 2934      *   of a Xen host.
 2935      *
 2936      * Additional information can be found in the Hypervisor CPUID
 2937      * Interface Proposal (https://lkml.org/lkml/2008/10/1/246)
 2938      */
 2939 
 2940     uint32_t eax, base;
 2941     union
 2942     {
 2943         uint32_t u[3];
 2944         char     s[13];
 2945     } sig = {{0}};
 2946 
 2947     /*
 2948      * For compatibility with other hypervisor interfaces, the Xen cpuid leaves
 2949      * can be found at the first otherwise unused 0x100 aligned boundary starting
 2950      * from 0x40000000.
 2951      *
 2952      * e.g If viridian extensions are enabled for an HVM domain, the Xen cpuid
 2953      * leaves will start at 0x40000100
 2954      */
 2955     for (base = 0x40000000; base < 0x40010000; base += 0x100)
 2956     {
 2957         Xen_Cpuid(base, &eax, &sig.u[0], &sig.u[1], &sig.u[2]);
 2958 
 2959         if (memcmp("XenVMMXenVMM", &sig.s[0], 12) == 0)
 2960         {
 2961             /* The highest basic calling parameter (largest value that EAX can
 2962              * be set to before calling CPUID) is returned in EAX. */
 2963             if ((eax - base) < 2)
 2964             {
 2965                 Log(LOG_LEVEL_DEBUG,
 2966                     "Insufficient Xen CPUID Leaves (eax=%x at base %x)",
 2967                     eax, base);
 2968                 return false;
 2969             }
 2970             Log(LOG_LEVEL_DEBUG,
 2971                 "Found Xen CPUID Leaf (eax=%x at base %x)", eax, base);
 2972             return true;
 2973         }
 2974     }
 2975 
 2976     return false;
 2977 }
 2978 
 2979 #endif /* XEN_CPUID_SUPPORT */
 2980 
 2981 static void GetCPUInfo(EvalContext *ctx)
 2982 {
 2983 #if defined(MINGW) || defined(NT)
 2984     Log(LOG_LEVEL_VERBOSE, "!! cpu count not implemented on Windows platform");
 2985     return;
 2986 #else
 2987     int count = 0;
 2988 
 2989     // http://preview.tinyurl.com/c9l2sh - StackOverflow on cross-platform CPU counting
 2990 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
 2991     // Linux, AIX, Solaris, Darwin >= 10.4
 2992     count = (int)sysconf(_SC_NPROCESSORS_ONLN);
 2993 #endif
 2994 
 2995 #if defined(HAVE_SYS_SYSCTL_H) && defined(HW_NCPU)
 2996     // BSD-derived platforms
 2997     int mib[2] = { CTL_HW, HW_NCPU };
 2998     size_t len;
 2999 
 3000     len = sizeof(count);
 3001     if(sysctl(mib, 2, &count, &len, NULL, 0) < 0)
 3002     {
 3003         Log(LOG_LEVEL_ERR, "!! failed to get cpu count: %s", strerror(errno));
 3004     }
 3005 #endif
 3006 
 3007 #ifdef HAVE_SYS_MPCTL_H
 3008 // Itanium processors have Intel Hyper-Threading virtual-core capability,
 3009 // and the MPC_GETNUMCORES_SYS operation counts each HT virtual core,
 3010 // which is equivalent to what the /proc/stat scan delivers for Linux.
 3011 //
 3012 // A build on 11i v3 PA would have the GETNUMCORES define, but if run on an
 3013 // 11i v1 system it would fail since that OS release has only GETNUMSPUS.
 3014 // So in the presence of GETNUMCORES, we check for an invalid arg error
 3015 // and fall back to GETNUMSPUS if necessary. An 11i v1 build would work
 3016 // normally on 11i v3, because on PA-RISC cores == spus since there's no
 3017 // HT on PA-RISC, and 11i v1 only runs on PA-RISC.
 3018 # ifdef MPC_GETNUMCORES_SYS
 3019     count = mpctl(MPC_GETNUMCORES_SYS, 0, 0);
 3020     if (count == -1 && errno == EINVAL)
 3021     {
 3022         count = mpctl(MPC_GETNUMSPUS_SYS, 0, 0);
 3023     }
 3024 # else
 3025     count = mpctl(MPC_GETNUMSPUS_SYS, 0, 0);    // PA-RISC processor count
 3026 # endif
 3027 #endif /* HAVE_SYS_MPCTL_H */
 3028 
 3029     if (count < 1)
 3030     {
 3031         Log(LOG_LEVEL_VERBOSE, "invalid processor count: %d", count);
 3032         return;
 3033     }
 3034     Log(LOG_LEVEL_VERBOSE, "Found %d processor%s", count, count > 1 ? "s" : "");
 3035 
 3036     char buf[CF_SMALLBUF] = "1_cpu";
 3037     if (count == 1)
 3038     {
 3039         EvalContextClassPutHard(ctx, buf, "source=agent,derived-from=sys.cpus");  // "1_cpu" from init - change if buf is ever used above
 3040         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cpus", "1", CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=CPU logical cores");
 3041     }
 3042     else
 3043     {
 3044         snprintf(buf, CF_SMALLBUF, "%d_cpus", count);
 3045         EvalContextClassPutHard(ctx, buf, "source=agent,derived-from=sys.cpus");
 3046         snprintf(buf, CF_SMALLBUF, "%d", count);
 3047         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "cpus", buf, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=CPU logical cores");
 3048     }
 3049 #endif /* MINGW || NT */
 3050 }
 3051 
 3052 /******************************************************************/
 3053 
 3054 // Implemented in Windows specific section.
 3055 #ifndef __MINGW32__
 3056 
 3057 /**
 3058    Return the number of seconds the system has been online given the current
 3059    time() as an argument, or return -1 if unavailable or unimplemented.
 3060 */
 3061 int GetUptimeSeconds(time_t now)
 3062 {
 3063     time_t boot_time = 0;
 3064     errno = 0;
 3065 
 3066 #if defined(BOOT_TIME_WITH_SYSINFO)         // Most GNU, Linux platforms
 3067 
 3068     struct sysinfo s;
 3069     if (sysinfo(&s) == 0)
 3070     {
 3071        // Don't return yet, sanity checking below
 3072        boot_time = now - s.uptime;
 3073     }
 3074 
 3075 #elif defined(BOOT_TIME_WITH_KSTAT)         // Solaris platform
 3076 
 3077     /* From command line you can get this with:
 3078        kstat -p unix:0:system_misc:boot_time */
 3079     kstat_ctl_t *kc = kstat_open();
 3080     if(kc != 0)
 3081     {
 3082         kstat_t *kp = kstat_lookup(kc, "unix", 0, "system_misc");
 3083         if(kp != 0)
 3084         {
 3085             if (kstat_read(kc, kp, NULL) != -1)
 3086             {
 3087                 kstat_named_t *knp = kstat_data_lookup(kp, "boot_time");
 3088                 if (knp != NULL)
 3089                 {
 3090                     boot_time = knp->value.ui32;
 3091                 }
 3092             }
 3093         }
 3094         kstat_close(kc);
 3095     }
 3096 
 3097 #elif defined(BOOT_TIME_WITH_PSTAT_GETPROC) // HP-UX platform only
 3098 
 3099     struct pst_status p;
 3100     if (pstat_getproc(&p, sizeof(p), 0, 1) == 1)
 3101     {
 3102         boot_time = (time_t)p.pst_start;
 3103     }
 3104 
 3105 #elif defined(BOOT_TIME_WITH_SYSCTL)        // BSD-derived platforms
 3106 
 3107     int mib[2] = { CTL_KERN, KERN_BOOTTIME };
 3108     struct timeval boot;
 3109     size_t len = sizeof(boot);
 3110     if (sysctl(mib, 2, &boot, &len, NULL, 0) == 0)
 3111     {
 3112         boot_time = boot.tv_sec;
 3113     }
 3114 
 3115 #elif defined(BOOT_TIME_WITH_PROCFS)
 3116 
 3117     struct stat p;
 3118     if (stat("/proc/1", &p) == 0)
 3119     {
 3120         boot_time = p.st_ctime;
 3121     }
 3122 
 3123 #elif defined(BOOT_TIME_WITH_UTMP)          /* SystemV, highly portable way */
 3124 
 3125     struct utmp query = { .ut_type = BOOT_TIME };
 3126     struct utmp *result;
 3127     setutent();
 3128     result = getutid(&query);
 3129     if (result != NULL)
 3130     {
 3131         boot_time = result->ut_time;
 3132     }
 3133     endutent();
 3134 
 3135 #elif defined(BOOT_TIME_WITH_UTMPX)                            /* POSIX way */
 3136 
 3137     struct utmpx query = { .ut_type = BOOT_TIME };
 3138     struct utmpx *result;
 3139     setutxent();
 3140     result = getutxid(&query);
 3141     if (result != NULL)
 3142     {
 3143         boot_time = result->ut_tv.tv_sec;
 3144     }
 3145     endutxent();
 3146 
 3147 #endif
 3148 
 3149     if(errno)
 3150     {
 3151         Log(LOG_LEVEL_VERBOSE, "boot time discovery error: %s", GetErrorStr());
 3152     }
 3153 
 3154     if(boot_time > now || boot_time <= 0)
 3155     {
 3156         Log(LOG_LEVEL_VERBOSE, "invalid boot time found; trying uptime command");
 3157         boot_time = GetBootTimeFromUptimeCommand(now);
 3158     }
 3159 
 3160     return boot_time > 0 ? now - boot_time : -1;
 3161 }
 3162 #endif // !__MINGW32__
 3163 
 3164 int GetUptimeMinutes(time_t now)
 3165 {
 3166     return GetUptimeSeconds(now) / SECONDS_PER_MINUTE;
 3167 }
 3168 
 3169 /******************************************************************/
 3170 
 3171 // Last resort: parse the output of the uptime command with a PCRE regexp
 3172 // and convert the uptime to boot time using "now" argument.
 3173 //
 3174 // The regexp needs to match all variants of the uptime command's output.
 3175 // Solaris 8/9/10:  10:45am up 109 day(s), 19:56, 1 user, load average:
 3176 // HP-UX 11.11:   9:24am  up 1 min,  1 user,  load average:
 3177 //                8:23am  up 23 hrs,  0 users,  load average:
 3178 //                9:33am  up 2 days, 10 mins,  0 users,  load average:
 3179 //                11:23am  up 2 days, 2 hrs,  0 users,  load average:
 3180 // Red Hat Linux: 10:51:23 up 5 days, 19:54, 1 user, load average:
 3181 //
 3182 // UPTIME_BACKREFS must be set to this regexp's maximum backreference
 3183 // index number (i.e., the count of left-parentheses):
 3184 #define UPTIME_REGEXP " up (\\d+ day[^,]*,|) *(\\d+( ho?u?r|:(\\d+))|(\\d+) min)"
 3185 #define UPTIME_BACKREFS 5
 3186 #define UPTIME_OVECTOR ((UPTIME_BACKREFS + 1) * 3)
 3187 
 3188 static time_t GetBootTimeFromUptimeCommand(time_t now)
 3189 {
 3190     FILE *uptimecmd;
 3191     pcre *rx;
 3192     int ovector[UPTIME_OVECTOR], i;
 3193     char *backref = NULL;
 3194     const char *uptimepath = "/usr/bin/uptime";
 3195     time_t uptime = 0;
 3196     const char *errptr;
 3197     int erroffset;
 3198 
 3199     rx = pcre_compile(UPTIME_REGEXP, 0, &errptr, &erroffset, NULL);
 3200     if (rx == NULL)
 3201     {
 3202         Log(LOG_LEVEL_DEBUG, "failed to compile regexp to parse uptime command");
 3203         return(-1);
 3204     }
 3205 
 3206     // Try "/usr/bin/uptime" first, then "/bin/uptime"
 3207     uptimecmd = cf_popen(uptimepath, "r", false);
 3208     uptimecmd = uptimecmd ? uptimecmd : cf_popen((uptimepath + 4), "r", false);
 3209     if (!uptimecmd)
 3210     {
 3211         Log(LOG_LEVEL_ERR, "uptime failed: (cf_popen: %s)", GetErrorStr());
 3212         return -1;
 3213     }
 3214 
 3215     size_t uptime_output_size = CF_SMALLBUF;
 3216     char *uptime_output = xmalloc(uptime_output_size);
 3217     i = CfReadLine(&uptime_output, &uptime_output_size, uptimecmd);
 3218 
 3219     cf_pclose(uptimecmd);
 3220     if (i == -1 && !feof(uptimecmd))
 3221     {
 3222         Log(LOG_LEVEL_ERR, "Reading uptime output failed. (getline: '%s')", GetErrorStr());
 3223         return -1;
 3224     }
 3225 
 3226     if ((i > 0) && (pcre_exec(rx, NULL, (const char *)uptime_output, i, 0, 0, ovector, UPTIME_OVECTOR) > 1))
 3227     {
 3228         for (i = 1; i <= UPTIME_BACKREFS ; i++)
 3229         {
 3230             if (ovector[i * 2 + 1] - ovector[i * 2] == 0) // strlen(backref)
 3231             {
 3232                 continue;
 3233             }
 3234             backref = uptime_output + ovector[i * 2];
 3235             // atoi() ignores non-digits, so no need to null-terminate backref
 3236 
 3237             time_t seconds;
 3238             switch(i)
 3239             {
 3240                 case 1: // Day
 3241                     seconds = SECONDS_PER_DAY;
 3242                     break;
 3243                 case 2: // Hour
 3244                     seconds = SECONDS_PER_HOUR;
 3245                     break;
 3246                 case 4: // Minute
 3247                 case 5:
 3248                     seconds = SECONDS_PER_MINUTE;
 3249                     break;
 3250                 default:
 3251                     seconds = 0;
 3252              }
 3253              uptime += ((time_t) atoi(backref)) * seconds;
 3254         }
 3255     }
 3256     else
 3257     {
 3258         Log(LOG_LEVEL_ERR, "uptime PCRE match failed: regexp: '%s', uptime: '%s'", UPTIME_REGEXP, uptime_output);
 3259     }
 3260     pcre_free(rx);
 3261     Log(LOG_LEVEL_VERBOSE, "Reading boot time from uptime command successful.");
 3262     return(uptime ? (now - uptime) : -1);
 3263 }
 3264 
 3265 /*****************************************************************************/
 3266 
 3267 /* TODO accept a const char * and move the ifdefs away from
 3268  *      evalfunction.c:FnCallGetUserInfo() to here. */
 3269 JsonElement* GetUserInfo(const void *passwd)
 3270 {
 3271 #ifdef __MINGW32__
 3272     return NULL;
 3273 
 3274 #else /* !__MINGW32__ */
 3275 
 3276     // we lose the const to set pw if passwd is NULL
 3277     struct passwd *pw = (struct passwd*) passwd;
 3278 
 3279     if (pw == NULL)
 3280     {
 3281         pw = getpwuid(getuid());
 3282     }
 3283 
 3284     if (pw == NULL)
 3285     {
 3286         return NULL;
 3287     }
 3288 
 3289     JsonElement *result = JsonObjectCreate(10);
 3290     JsonObjectAppendString(result, "username", pw->pw_name);
 3291     JsonObjectAppendString(result, "description", pw->pw_gecos);
 3292     JsonObjectAppendString(result, "home_dir", pw->pw_dir);
 3293     JsonObjectAppendString(result, "shell", pw->pw_shell);
 3294     JsonObjectAppendInteger(result, "uid", pw->pw_uid);
 3295     JsonObjectAppendInteger(result, "gid", pw->pw_gid);
 3296     //JsonObjectAppendBool(result, "locked", IsAccountLocked(pw->pw_name, pw));
 3297     // TODO: password: { format: "hash", data: { ...GetPasswordHash()... } }
 3298     // TODO: group_primary: name of group
 3299     // TODO: groups_secondary: [ names of groups ]
 3300     // TODO: gids_secondary: [ gids of groups ]
 3301 
 3302     return result;
 3303 #endif
 3304 }
 3305 
 3306 /*****************************************************************************/
 3307 
 3308 void GetSysVars(EvalContext *ctx)
 3309 {
 3310     /* Get info for current user. */
 3311     JsonElement *info = GetUserInfo(NULL);
 3312     if (info != NULL)
 3313     {
 3314         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "user_data",
 3315                                       info, CF_DATA_TYPE_CONTAINER,
 3316                                       "source=agent,user_info");
 3317         JsonDestroy(info);
 3318     }
 3319 }
 3320 
 3321 /*****************************************************************************/
 3322 
 3323 void GetDefVars(EvalContext *ctx)
 3324 {
 3325     EvalContextVariablePutSpecial(
 3326         ctx, SPECIAL_SCOPE_DEF, "jq",
 3327         "jq --compact-output --monochrome-output --ascii-output --unbuffered --sort-keys",
 3328         CF_DATA_TYPE_STRING, "invocation,source=agent,command_name=jq");
 3329 }
 3330 /*****************************************************************************/
 3331 
 3332 void DetectEnvironment(EvalContext *ctx)
 3333 {
 3334     GetNameInfo3(ctx);
 3335     GetInterfacesInfo(ctx);
 3336     GetNetworkingInfo(ctx);
 3337     Get3Environment(ctx);
 3338     BuiltinClasses(ctx);
 3339     OSClasses(ctx);
 3340     GetSysVars(ctx);
 3341     GetDefVars(ctx);
 3342 }