mirror of
https://github.com/Stichting-MINIX-Research-Foundation/pkgsrc-ng.git
synced 2025-09-10 04:50:03 -04:00
355 lines
8.1 KiB
C
355 lines
8.1 KiB
C
/* $NetBSD: kver.c,v 1.12 2013/01/04 06:00:48 apb Exp $ */
|
|
|
|
#define sysctl _sysctl
|
|
#define uname _uname
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/utsname.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <err.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
|
|
#undef sysctl
|
|
#undef uname
|
|
|
|
|
|
#define KVER_VERSION_FMT "%s %s (LIBKVER) #0: Tue Jan 19 00:00:00 UTC 2038 root@localhost:/sys/arch/%s/compile/LIBKVER"
|
|
|
|
#define KVER_NOT_INITIALIZED_OSRELEASE -2
|
|
#define KVER_INVALID_OSRELEASE -1
|
|
|
|
/*
|
|
* How to override the operating system name; e.g. "NetBSD".
|
|
* Used by sysctl kern.ostype, uname -s, struct utsname.sysname
|
|
*/
|
|
#ifndef PATH_LIBKVER_OSTYPE
|
|
#define PATH_LIBKVER_OSTYPE "/libkver_ostype"
|
|
#endif
|
|
#ifndef VAR_LIBKVER_OSTYPE
|
|
#define VAR_LIBKVER_OSTYPE "LIBKVER_OSTYPE"
|
|
#endif
|
|
|
|
/*
|
|
* How to override the operating system release level; e.g. "6.0_STABLE".
|
|
* Used by sysctl kern.osrelease, uname -r, struct utsname.release
|
|
*
|
|
* This is also coverted to an integer, e.g. 601000000,
|
|
* for use by sysctl.kern.osrevision.
|
|
*/
|
|
#ifndef PATH_LIBKVER_OSRELEASE
|
|
#define PATH_LIBKVER_OSRELEASE "/libkver_osrelease"
|
|
#endif
|
|
#ifndef VAR_LIBKVER_OSRELEASE
|
|
#define VAR_LIBKVER_OSRELEASE "LIBKVER_OSRELEASE"
|
|
#endif
|
|
|
|
/*
|
|
* How to override the machine hardware platform, e.g. "i386".
|
|
* Used by sysctl hw.machine, uname -m, struct utsname.machine.
|
|
*/
|
|
#ifndef PATH_LIBKVER_MACHINE
|
|
#define PATH_LIBKVER_MACHINE "/libkver_machine"
|
|
#endif
|
|
#ifndef VAR_LIBKVER_MACHINE
|
|
#define VAR_LIBKVER_MACHINE "LIBKVER_MACHINE"
|
|
#endif
|
|
|
|
/*
|
|
* How to override the machine processor architecture, e.g. "i386".
|
|
* Used by sysctl hw.machine_arch, uname -p.
|
|
*/
|
|
#ifndef PATH_LIBKVER_MACHINE_ARCH
|
|
#define PATH_LIBKVER_MACHINE_ARCH "/libkver_machine_arch"
|
|
#endif
|
|
#ifndef VAR_LIBKVER_MACHINE_ARCH
|
|
#define VAR_LIBKVER_MACHINE_ARCH "LIBKVER_MACHINE_ARCH"
|
|
#endif
|
|
|
|
static struct kver {
|
|
char ostype[_SYS_NMLN];
|
|
char osrelease[_SYS_NMLN];
|
|
int osrevision;
|
|
char version[_SYS_NMLN];
|
|
char machine[_SYS_NMLN];
|
|
char machine_arch[_SYS_NMLN];
|
|
} kver = {
|
|
.osrevision = KVER_NOT_INITIALIZED_OSRELEASE
|
|
};
|
|
|
|
static struct utsname real_utsname;
|
|
|
|
#define KVER_NOT_INITIALIZED \
|
|
(kver.osrevision == KVER_NOT_INITIALIZED_OSRELEASE)
|
|
|
|
#define KVER_BADLY_INITIALIZED (kver.osrevision == KVER_INVALID_OSRELEASE)
|
|
|
|
#define SYSCTL_STRING(oldp, oldlenp, str) \
|
|
if (oldlenp) { \
|
|
size_t len = strlen(str) + 1; \
|
|
if (!oldp) \
|
|
*oldlenp = len; \
|
|
else { \
|
|
if (*oldlenp < len) { \
|
|
r = ENOMEM; \
|
|
len = *oldlenp; \
|
|
} else \
|
|
*oldlenp = len; \
|
|
(void) strncpy(oldp, str, len); \
|
|
} \
|
|
}
|
|
|
|
#if __NetBSD_Version__ >= 399001600
|
|
#define SYSCTL_CONST const
|
|
#else
|
|
#define SYSCTL_CONST
|
|
#endif
|
|
|
|
static int
|
|
str2osrevision(char *s)
|
|
{
|
|
char c;
|
|
int n, r = 0;
|
|
|
|
if (s == NULL || *s == 0)
|
|
return KVER_INVALID_OSRELEASE;
|
|
|
|
if (!isdigit(*s))
|
|
return KVER_INVALID_OSRELEASE;
|
|
|
|
/* first digit: major */
|
|
for (n = 0; isdigit(*s); s++) {
|
|
n = (n * 10) + (*s - '0');
|
|
}
|
|
if (*s == 0 || *s != '.')
|
|
return KVER_INVALID_OSRELEASE;
|
|
r += (n * 100000000);
|
|
|
|
/* second digit: minor */
|
|
for (s++, n = 0; isdigit(*s); s++) {
|
|
n = (n * 10) + (*s - '0');
|
|
}
|
|
r += (n * 1000000);
|
|
|
|
/* nothing more, return */
|
|
if (*s == 0)
|
|
return r;
|
|
|
|
/* optional third digit: patchlevel */
|
|
if (*s == '.') {
|
|
for (s++, n = 0; isdigit(*s); s++) {
|
|
n = (n * 10) + (*s - '0');
|
|
}
|
|
r += (n * 100);
|
|
}
|
|
/* nothing more, return */
|
|
if (*s == 0)
|
|
return r;
|
|
/* or optional underscore followed by release status */
|
|
if (*s == '_')
|
|
/* ignore the trailing label */
|
|
return r;
|
|
/* or optional letters: release */
|
|
n = 0;
|
|
c = 'Z';
|
|
while (*s >= 'A' && *s <= 'Z') {
|
|
if (c != 'Z' && *s != 0)
|
|
break;
|
|
c = *s;
|
|
n += *s++ - '@';
|
|
}
|
|
if (n > 99)
|
|
return KVER_INVALID_OSRELEASE;
|
|
if (*s == 0)
|
|
return r + (n * 10000);
|
|
|
|
/* return on error if we end up here */
|
|
return KVER_INVALID_OSRELEASE;
|
|
}
|
|
|
|
/*
|
|
* Initialise one element of struct kver.
|
|
*
|
|
* The result is based on getenv(envvarname), or readlink(linkname),
|
|
* or defaultval. If the provided defaultval is NULL, then the empty
|
|
* string is used.
|
|
*
|
|
* Returns 1 if a non-default value was used, 0 if a default was used.
|
|
*/
|
|
static int
|
|
kver_init_var(char *dst, size_t dstlen, const char *envvar,
|
|
const char *linkname, const char *defaultval)
|
|
{
|
|
ssize_t len;
|
|
char *v;
|
|
|
|
v = getenv(envvar);
|
|
if (v) {
|
|
(void) strlcpy(dst, v, dstlen);
|
|
return 1;
|
|
}
|
|
len = readlink(linkname, dst, dstlen);
|
|
if (len > 0) {
|
|
dst[len] = '\0';
|
|
return 1;
|
|
}
|
|
if (defaultval) {
|
|
(void) strlcpy(dst, defaultval, dstlen);
|
|
return 0;
|
|
}
|
|
dst[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
kver_initialize(void)
|
|
{
|
|
char *v;
|
|
int i;
|
|
int nok = 0;
|
|
char buf[PATH_MAX + 1];
|
|
kver.osrevision = KVER_INVALID_OSRELEASE; /* init done */
|
|
|
|
if (_uname(&real_utsname) != 0) {
|
|
warn("libkver: uname");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* For each variable that can be overridden: try the
|
|
* environment variable, then try the symlink, or fall back
|
|
* to the unmodified (existing) value.
|
|
*/
|
|
nok += kver_init_var(kver.ostype, sizeof(kver.ostype),
|
|
VAR_LIBKVER_OSTYPE, PATH_LIBKVER_OSTYPE,
|
|
real_utsname.sysname);
|
|
nok += kver_init_var(kver.osrelease, sizeof(kver.osrelease),
|
|
VAR_LIBKVER_OSRELEASE, PATH_LIBKVER_OSRELEASE,
|
|
real_utsname.release);
|
|
nok += kver_init_var(kver.machine, sizeof(kver.machine),
|
|
VAR_LIBKVER_MACHINE, PATH_LIBKVER_MACHINE,
|
|
real_utsname.machine);
|
|
nok += kver_init_var(kver.machine_arch, sizeof(kver.machine_arch),
|
|
VAR_LIBKVER_MACHINE_ARCH, PATH_LIBKVER_MACHINE_ARCH,
|
|
"");
|
|
|
|
/*
|
|
* warning if the default was used for all variables. no warning
|
|
* if at least one non-default value was used.
|
|
*/
|
|
if (nok == 0) {
|
|
warnx("libkver: not configured");
|
|
return;
|
|
}
|
|
|
|
/* machine_arch is not in struct utsname, so get default from sysctl */
|
|
if (kver.machine_arch[0] == '\0') {
|
|
int mib[] = { CTL_HW, HW_MACHINE_ARCH, 0 };
|
|
size_t len = sizeof(kver.machine_arch);
|
|
int err;
|
|
|
|
err = _sysctl(mib, 2, kver.machine_arch, &len, NULL, 0);
|
|
if (err < 0) {
|
|
warn("libkver: sysctl hw.machine_arch");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* combine several strings to create kver.version */
|
|
(void) snprintf(kver.version, sizeof(kver.version), KVER_VERSION_FMT,
|
|
kver.ostype, kver.osrelease, kver.machine);
|
|
|
|
/*
|
|
* Convert string osrelease to integer osrevision.
|
|
* This must be the last thing we do, so that any failure
|
|
* resuls in kver.osrelease being set to KVER_INVALID_OSRELEASE.
|
|
*/
|
|
kver.osrevision = str2osrevision(kver.osrelease);
|
|
if (kver.osrevision == KVER_INVALID_OSRELEASE) {
|
|
warnx("libkver: cannot convert osrelease to osrevision: %s",
|
|
kver.osrelease);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int
|
|
sysctl(SYSCTL_CONST int *name, u_int namelen, void *oldp, size_t * oldlenp,
|
|
const void *newp, size_t newlen)
|
|
{
|
|
int r = 0;
|
|
|
|
_DIAGASSERT(name != NULL);
|
|
|
|
if (newp != (void *) NULL)
|
|
goto real;
|
|
|
|
if (KVER_NOT_INITIALIZED)
|
|
kver_initialize();
|
|
|
|
if (KVER_BADLY_INITIALIZED || namelen != 2)
|
|
goto real;
|
|
|
|
switch (name[0]) {
|
|
case CTL_KERN: {
|
|
switch (name[1]) {
|
|
case KERN_OSTYPE:
|
|
SYSCTL_STRING(oldp, oldlenp, kver.ostype);
|
|
return (r);
|
|
case KERN_OSRELEASE:
|
|
SYSCTL_STRING(oldp, oldlenp, kver.osrelease);
|
|
return (r);
|
|
case KERN_OSREV:
|
|
if (oldlenp) {
|
|
if (!oldp)
|
|
*oldlenp = sizeof(int);
|
|
else {
|
|
if (*oldlenp < sizeof(int))
|
|
return (ENOMEM);
|
|
*oldlenp = sizeof(int);
|
|
*((int *) oldp) = kver.osrevision;
|
|
}
|
|
}
|
|
return (r);
|
|
case KERN_VERSION:
|
|
SYSCTL_STRING(oldp, oldlenp, kver.version);
|
|
return (r);
|
|
}
|
|
}
|
|
case CTL_HW: {
|
|
switch (name[1]) {
|
|
case HW_MACHINE:
|
|
SYSCTL_STRING(oldp, oldlenp, kver.machine);
|
|
return (r);
|
|
case HW_MACHINE_ARCH:
|
|
SYSCTL_STRING(oldp, oldlenp, kver.machine_arch);
|
|
return (r);
|
|
}
|
|
}
|
|
}
|
|
real: return (_sysctl(name, namelen, oldp, oldlenp, newp, newlen));
|
|
}
|
|
|
|
int
|
|
uname(struct utsname * n)
|
|
{
|
|
if (KVER_NOT_INITIALIZED)
|
|
kver_initialize();
|
|
|
|
if (KVER_BADLY_INITIALIZED)
|
|
return _uname(n);
|
|
|
|
(void) strncpy(n->sysname, kver.ostype, _SYS_NMLN);
|
|
(void) strncpy(n->nodename, real_utsname.nodename, _SYS_NMLN);
|
|
(void) strncpy(n->release, kver.osrelease, _SYS_NMLN);
|
|
(void) strncpy(n->version, kver.version, _SYS_NMLN);
|
|
(void) strncpy(n->machine, kver.machine, _SYS_NMLN);
|
|
return 0;
|
|
}
|