Decoded: uname (coreutils)

[Back to Project Main Page]

Note: This page explores the design of command-line utilities. It is not a user guide.
[GNU Manual] [POSIX requirement] [Linux man] [FreeBSD man]

Logical flow of uname command (coreutils)

Summary

uname - print system information

[Source] [Code Walkthrough]

Lines of code: 377
Principal syscall: uname()
Support syscalls: None
Options: 22 (9 short and 13 long)

Spirtually linked to uname introduced in System V (1985)
Added to Shellutils in November 1992 [First version]
Number of revisions: 102 [Code Evolution]

Helpers:
  • decode_switches() - Parses switches to set toprint
  • print_element() - Prints the input data
External non-standard helpers:
  • die() - Exit with mandatory non-zero error and message to stderr
  • error() - Outputs error message to standard error with possible process termination
  • sysctl() - Access kernel data at runtime
  • sysctrlbyname() - Access kernel data by field at runtime
  • sysinfo() - Gets system info strings (SunOS)

Setup

uname has several layers of initialization prior to main().

First, uname starts with a two-line file which sets the mode to UNAME_UNAME (similar to 'arch'). This means we'll process all options during parsing.

Second, the preprocessor determines which machine-dependent headers to include so that uname knows which functions to call for the non-portable features. The major variants mentioned are BSD, and Mach (Apple).

main() initializes the following:

  • toprint - Integer bitfield of printing options
  • name - Contains the return value of uname() syscall

Parsing

Parsing is simple: What information should we collect and print. This is done by OR-masking the toprint integer to reflect data. Each option reflects a bit. Invoking -a asserts all bits via UINT_MAX.

Parsing failures

Parsing can only fail if an unknown option is used

This failure result in a short error message followed by the usage instructions.

Execution

The first step is to get the system information via the uname() syscall. This returns a utsname structure containing most of the data that we need. These data are portable across POSIX compliant systems.

Now mask the bitfield for each of the fields we're interested in and print via puts(). The fields should appear one after the next on a single line. Now me move to the non-portable information.

Non-portable fields

Two fields that require architecture specific techniques are the processor type and the hardware platform.

Processor
We try three ways to get the processor architecture until success:
  • A sysinfo() syscall. This three argument variant is specific to Sun/Oracle Unix.
  • A sysctl() syscall. Specific for BSD-based systems.
  • A sysctrlbyname() for systems based on the Mach kernel (Apple). Apparently sysctl() isn't enough.
Platform
Platform information follows the same as the processor, except no work-around needed for Mach. First, we attempt sysinfo(), then we attempt sysctl().


Operating System

The final check is to pull the operating system information from HOST_OPERATING_SYSTEM, which should have been generated in your system's config.h.

These non-portable checks cannot fail, they simply result in an output of 'unknown'

Failure case: If the uname() syscall fails, the uname utility cannot complete


[Back to Project Main Page]