Mark Harrison
Mark Harrison

Reputation: 304662

OSX: programmatically get uptime?

Something similar to linux

cat /proc/uptime

which returns the uptime in seconds, and preferably not parsing uptime(1).

Upvotes: 13

Views: 11589

Answers (9)

Gad D Lord
Gad D Lord

Reputation: 6782

For the benefit of Delphi developers:

First we would declare a function ExecuteCommand which can run a command such as 'sysctl kern.boottime' and get its output.

We would use the libC functions popen, fgets, and pclose

const libc = '/usr/lib/libc.dylib';

/// Man Page: http://man7.org/linux/man-pages/man3/popen.3.html
function popen(const command: MarshaledAString; const _type: MarshaledAString): TStreamHandle; cdecl; external libc name _PU + 'popen';

/// Man Page: http://man7.org/linux/man-pages/man3/pclose.3p.html
function pclose(filehandle: TStreamHandle): Int32; cdecl; external libc name _PU + 'pclose';

/// Man Page: http://man7.org/linux/man-pages/man3/fgets.3p.html
function fgets(Buffer: Pointer; Size: Int32; Stream: TStreamHAndle): Pointer; cdecl; external libc name _PU + 'fgets';

/// Utility function to return a buffer of ASCII-Z data as a string.
function BufferToString(Buffer: Pointer; MaxSize: UInt32): string;
var
  Cursor: ^UInt8;
  EndOfBuffer: NativeUInt;
begin
  Result := EmptyStr;
  if not Assigned(Buffer) then
    Exit;
  Cursor := Buffer;
  EndOfBuffer := NativeUint(Cursor) + MaxSize;
  while (NativeUint(Cursor) < EndOfBuffer) and (Cursor^ <> 0) do
  begin
    Result := Result + Chr(Cursor^);
    Cursor := Pointer(Succ(NativeUInt(Cursor)));
  end;
end;

{$if defined(POSIX)}
// URL https://chapmanworld.com/calling-linux-commands-from-delphi/
function ExecuteCommand(const CommandLine: AnsiString): string;
var
  Handle: TStreamHandle;
  Data: array[0..511] of UInt8;
begin
  Handle := popen(PAnsiChar(CommandLine), 'r');
  try
    Result := EmptyStr;
    while fgets(@data[0], Sizeof(Data), Handle) <> nil do
      Result := Result + BufferToString(@Data[0], SizeOf(Data));
  finally
    pclose(Handle);
  end;
end;
{$endif}

This would return a value such as:

{ sec = 1730143666, usec = 758303 } Mon Oct 28 21:27:46 2024

Now lets declare a function that can extract a named regex group:

function GetFirstValue(const Text, Mask, GroupName: string): string;
begin
  Result := EmptyStr;
  var MC := TRegex.Matches(Text, Mask, [TRegexOption.roIgnoreCase, TRegexOption.roMultiLine, TRegexOption.roCompiled, TRegexOption.roExplicitCapture]);
  var M := Default(TMatch);
  for M in MC do
  begin
    Result := M.Groups[GroupName].Value;
    Break;
  end;
end;

And also declare a function that returns a Delphi TDateTime from Unix epoch (number of seconds since 1/1/1970):

function FromUnix(const Value: Int64): TDateTime;
// Sets UnixStartDate to TDateTime of 01/01/1970
const UnixStartDate: TDateTime = 25569.0; // EncodeDate(1970, 1, 1)
begin
  Result := (Value / 86400) + UnixStartDate;
end;

And you call it like:

var Output := ExecuteCommand(AnsiString('sysctl -n kern.boottime'));
var Seconds := GetFirstValue(Output, 'sec\s=\s(?<sec>\d+),', 'sec');
var BootTime := TTimestamps.FromUnix(StrToInt64(Seconds));

BootTime holds the TDateTime value of when the OS was booted.

Upvotes: 0

luckman212
luckman212

Reputation: 801

To add to @Bleyddyn's answer (since it's 11 years and counting)...

I needed a small utility to print the uptime in seconds, so I took that code and slightly altered it to compile on modern macOS.

You can clone the luckman212/uptime_s repo and run ./build.sh to generate your own universal binary that will simply print the uptime in secs.

Upvotes: 1

Maxim Kholyavkin
Maxim Kholyavkin

Reputation: 4573

correct way:

CFTimeInterval getSystemUptime(void)
{
    enum { NANOSECONDS_IN_SEC = 1000 * 1000 * 1000 };
    static double multiply = 0;
    if (multiply == 0)
    {
        mach_timebase_info_data_t s_timebase_info;
        kern_return_t result = mach_timebase_info(&s_timebase_info);
        assert(result == noErr);
        // multiply to get value in the nano seconds
        multiply = (double)s_timebase_info.numer / (double)s_timebase_info.denom;
        // multiply to get value in the seconds
        multiply /= NANOSECONDS_IN_SEC;
    }
    return mach_absolute_time() * multiply;
}

also you could use CACurrentMediaTime() from QuartzCore.framework (which has same code probably) - Available since OS X v10.5 and iOS 2.0

Upvotes: 1

Bleyddyn
Bleyddyn

Reputation: 387

Old question, I know, but I needed to do the same thing so I thought I'd post the code I'm using, which I got from http://cocoadev.com/wiki/FindingUptime

#include <time.h>
#include <errno.h>
#include <sys/sysctl.h>

double uptime()
{
    struct timeval boottime;
    size_t len = sizeof(boottime);
    int mib[2] = { CTL_KERN, KERN_BOOTTIME };
    if( sysctl(mib, 2, &boottime, &len, NULL, 0) < 0 )
    {
        return -1.0;
    }
    time_t bsec = boottime.tv_sec, csec = time(NULL);

    return difftime(csec, bsec);
}

Upvotes: 19

Jon
Jon

Reputation: 1489

If anyone is trying to do this programmatically using sysctl.h and is expecting a string back like what you see in the command line, the returned value that I get is a 16 byte array, not a string:

sysctlbyname("kern.boottime", value, &size, NULL, 0);

An example for what gets put into value in hex starting from the [0] index:

a9 af c6 4e 0 0 0 0 0 0 0 0 28 be 92 55

The first 4 bytes (maybe the first 8, won't know until Jan 2012) is the epoch time in little endian byte order.

Upvotes: 2

Valerio Schiavoni
Valerio Schiavoni

Reputation: 1463

A simple Lua script to do exactly what you ask for:

local now=tonumber(io.popen("date +%s"):read())
local boottime=tonumber(io.popen("sysctl -n kern.boottime"):read():match("sec = (%d+)"))
local uptime=now-boottime

Upvotes: 0

Uri
Uri

Reputation: 1

Unfortunately the "sysctl kern.boottime" returns the seconds of the timestamp, not elapsed seconds.. Multiple calls do not increase the second count, but must be seconds from epoc of the boot date itself.

Upvotes: 0

JWWalker
JWWalker

Reputation: 22717

There is a function UpTime declared in DriverServices.h. I believe this is equivalent to another function mach_absolute_time. Both seem to be undocumented.

Upvotes: 0

Shaggy Frog
Shaggy Frog

Reputation: 27601

The Uptime article on Wikipedia has an interesting lead:

Using sysctl

There is also a method of using sysctl to call the system's last boot time: $ sysctl kern.boottime kern.boottime: { sec = 1271934886, usec = 667779 } Thu Apr 22 12:14:46 2010

Which references sysctl(8), which references sysctl(3).

Upvotes: 17

Related Questions