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