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