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