#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/varsym.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MFLAG 0x0001
#define NFLAG 0x0002
#define PFLAG 0x0004
#define RFLAG 0x0008
#define SFLAG 0x0010
#define VFLAG 0x0020
#define IFLAG 0x0040
#define UFLAG 0x0080
#define KFLAG 0x0100
#define GFLAG 0x0200
#define GFLAG2 0x0400
#define BFLAG 0x0800
typedef void (*get_t)(void);
static get_t get_ident, get_machine, get_hostname, get_arch;
static get_t get_release, get_sysname, get_version, get_pkgabi;
static get_t get_kernvers, get_uservers;
static get_t get_buildid;
static void native_ident(void);
static void native_machine(void);
static void native_hostname(void);
static void native_arch(void);
static void native_release(void);
static void native_sysname(void);
static void native_version(void);
static void native_pkgabi(void);
static void native_kernvers(void);
static void native_uservers(void);
static void native_buildid(void);
static void print_uname(void);
static void setup_get(void);
static void usage(void) __dead2;
static char *ident, *machine, *hostname, *arch;
static char *release, *sysname, *version, *pkgabi;
static char *kernvers, *uservers;
static char *buildid;
static int space;
static u_int flags;
int
main(int argc, char *argv[])
{
int ch;
setup_get();
while ((ch = getopt(argc, argv, "abiKmnprsUvP")) != -1) {
switch(ch) {
case 'a':
flags |= (MFLAG | NFLAG | RFLAG | SFLAG | VFLAG);
break;
case 'b':
flags |= BFLAG;
break;
case 'i':
flags |= IFLAG;
break;
case 'K':
flags |= KFLAG;
break;
case 'm':
flags |= MFLAG;
break;
case 'n':
flags |= NFLAG;
break;
case 'p':
flags |= PFLAG;
break;
case 'r':
flags |= RFLAG;
break;
case 's':
flags |= SFLAG;
break;
case 'U':
flags |= UFLAG;
break;
case 'v':
flags |= VFLAG;
break;
case 'P':
if (flags & GFLAG)
flags |= GFLAG2;
flags |= GFLAG;
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc)
usage();
if (!flags)
flags |= SFLAG;
print_uname();
exit(0);
}
static
void
CHECK_ENV(const char *envname, get_t *getp, get_t nativep, char **varp)
{
char buf[MAXVARSYM_DATA];
if ((*varp = getenv(envname)) == NULL) {
if (varsym_get(VARSYM_ALL_MASK, envname,
buf, sizeof(buf)) < 0) {
*getp = nativep;
return;
}
*varp = strdup(buf);
}
*getp = NULL;
}
static void
setup_get(void)
{
CHECK_ENV("UNAME_s", &get_sysname, native_sysname, &sysname);
CHECK_ENV("UNAME_n", &get_hostname, native_hostname, &hostname);
CHECK_ENV("UNAME_r", &get_release, native_release, &release);
CHECK_ENV("UNAME_v", &get_version, native_version, &version);
CHECK_ENV("UNAME_m", &get_machine, native_machine, &machine);
CHECK_ENV("UNAME_p", &get_arch, native_arch, &arch);
CHECK_ENV("UNAME_i", &get_ident, native_ident, &ident);
CHECK_ENV("UNAME_K", &get_kernvers, native_kernvers, &kernvers);
CHECK_ENV("UNAME_U", &get_uservers, native_uservers, &uservers);
CHECK_ENV("UNAME_G", &get_pkgabi, native_pkgabi, &pkgabi);
CHECK_ENV("UNAME_b", &get_buildid, native_buildid, &buildid);
}
#define PRINT_FLAG(flags,flag,var) \
if ((flags & flag) == flag) { \
if (space) \
printf(" "); \
else \
space++; \
if (get_##var != NULL) \
(*get_##var)(); \
printf("%s", var); \
}
static void
print_uname(void)
{
PRINT_FLAG(flags, SFLAG, sysname);
PRINT_FLAG(flags, NFLAG, hostname);
PRINT_FLAG(flags, RFLAG, release);
PRINT_FLAG(flags, VFLAG, version);
PRINT_FLAG(flags, MFLAG, machine);
PRINT_FLAG(flags, PFLAG, arch);
PRINT_FLAG(flags, IFLAG, ident);
PRINT_FLAG(flags, KFLAG, kernvers);
PRINT_FLAG(flags, UFLAG, uservers);
PRINT_FLAG(flags, GFLAG, pkgabi);
PRINT_FLAG(flags, BFLAG, buildid);
printf("\n");
}
#define NATIVE_SYSCTL2_GET(var, mib0, mib1) \
static void \
native_##var(void) \
{ \
int mib[] = { (mib0), (mib1) }; \
size_t len; \
static char buf[1024]; \
char **varp = &(var); \
\
len = sizeof buf; \
if (sysctl(mib, NELEM(mib), &buf, &len, NULL, 0) == -1) \
err(1, "sysctl(%s,%s)", #mib0, #mib1);
#define NATIVE_SYSCTLNAME_GET(var, name) \
static void \
native_##var(void) \
{ \
size_t len; \
static char buf[1024]; \
char **varp = &(var); \
\
len = sizeof buf; \
if (sysctlbyname(name, &buf, &len, NULL, 0) == -1) \
err(1, "sysctlbyname(%s)", name);
#define NATIVE_SET \
*varp = buf; \
return; \
} struct __hack
#define NATIVE_BUFFER (buf)
#define NATIVE_LENGTH (len)
NATIVE_SYSCTL2_GET(sysname, CTL_KERN, KERN_OSTYPE) {
} NATIVE_SET;
NATIVE_SYSCTL2_GET(hostname, CTL_KERN, KERN_HOSTNAME) {
} NATIVE_SET;
NATIVE_SYSCTL2_GET(release, CTL_KERN, KERN_OSRELEASE) {
} NATIVE_SET;
NATIVE_SYSCTL2_GET(version, CTL_KERN, KERN_VERSION) {
size_t n;
char *p;
p = NATIVE_BUFFER;
n = NATIVE_LENGTH;
for (; n--; ++p)
if (*p == '\n' || *p == '\t')
*p = ' ';
} NATIVE_SET;
NATIVE_SYSCTL2_GET(machine, CTL_HW, HW_MACHINE) {
} NATIVE_SET;
NATIVE_SYSCTL2_GET(arch, CTL_HW, HW_MACHINE_ARCH) {
} NATIVE_SET;
NATIVE_SYSCTLNAME_GET(ident, "kern.ident") {
} NATIVE_SET;
NATIVE_SYSCTLNAME_GET(buildid, "kern.build_id") {
} NATIVE_SET;
static void
native_pkgabi(void)
{
char osrel[64];
char mach[64];
size_t len;
double d;
len = sizeof(osrel);
if (sysctlbyname("kern.osrelease", osrel, &len, NULL, 0) == -1)
err(1, "sysctlbyname");
len = sizeof(mach);
if (sysctlbyname("hw.machine", mach, &len, NULL, 0) == -1)
err(1, "sysctlbyname");
d = strtod(osrel, NULL);
if ((flags & GFLAG2) == 0) {
if ((int)(d * 10) & 1)
d = d + 0.1;
}
if (strcmp(mach, "x86_64") == 0)
snprintf(mach, sizeof(mach), "x86:64");
else if (strcmp(mach, "i386") == 0)
snprintf(mach, sizeof(mach), "x86:32");
asprintf(&pkgabi, "dragonfly:%3.1f:%s", d, mach);
}
static void
native_kernvers(void)
{
static char buf[128];
snprintf(buf, sizeof(buf), "%d", getosreldate());
kernvers = buf;
}
static void
native_uservers(void)
{
static char buf[128];
snprintf(buf, sizeof(buf), "%d", __DragonFly_version);
uservers = buf;
}
static void
usage(void)
{
fprintf(stderr, "usage: uname [-abiKmnprsUvP]\n");
exit(1);
}