Get the Boot Time of a Linux Machine
· May 11, 08:38 AMThe boot time and uptime of a Linux computer are available from the /proc filesystem. /proc/stat returns, among other information, the boot time in seconds since the Unix epoch. /proc/uptime returns the number of seconds the system has been up.
Perhaps the easiest way to get this information is to execute the shell command cat and extract the time data from the output. Here is the output from a terminal session.
foo@bar:~$ cat /proc/uptime 5377546.89 4696377.57
The first time is the uptime of the system; the second is the time the machine has spent idle.
Here is some REALbasic code to get the time.
dim s as new Shell
s.Execute "cat /proc/uptime"
dim uptimeSeconds as Double = Val(NthField(s.Result, " " , 1))
dim uptime as new Date
uptime.TotalSeconds = uptime.TotalSeconds - uptimeSeconds
Now let’s refactor this by isolating the code that parses the shell output. This makes it easier to write unit tests for this bit of code, and to easily change the parsing code if, for instance, I learn how to use regular expressions.
Function ParseUptime(s as String) as String
return NthField(s, " ", 1)
End Function
Now the code above becomes this.
dim s as new Shell
s.Execute "cat /proc/uptime"
dim uptimeSeconds as Double = Val(ParseUptime(s.Result))
dim uptime as new Date
uptime.TotalSeconds = uptime.TotalSeconds - uptimeSeconds
Something is still missing — error checking. I expect /proc/uptime to be available, but I cannot guarantee it. I don’t know how to handle an error, because I don’t know how this code is to be used. So I raise a ShellError and let the caller deal with it.
dim uptime as Date
dim s as new Shell
s.Execute "cat /proc/uptime"
if s.ErrorCode = 0 then
dim uptimeSeconds as Double = Val(ParseUptime(s.Result))
uptime = new Date
uptime.TotalSeconds = uptime.TotalSeconds - uptimeSeconds
else
raise new ShellError(s)
end if
The error-checking logic is code that needs to be written for each invocation of Shell.Execute. That is to say, it will be duplicated code, and duplication should usually be refactored out.
The part of the code that varies is the code that parses the shell output. It already lives in a separate method; thus we can pass it as a delegate.
So let us first define a module ShellExtension. To it, add the following delegate declaration.
Delegate Function ShellOutputParser(s as String) As String
Next is a method that extends the Shell class.
Function Execute(extends s as Shell, command as String, outputParser as ShellOutputParser=nil) As String
if outputParser = nil then
outputParser = TrivialParser
end if
s.Execute command
if s.ErrorCode = 0 then
return outputParser.Invoke(s.Result)
else
raise new ShellError(s)
end if
End Function
The outputParser parameter has default value Nil. In this case, the method uses a trivial parsing function that returns the string passed to it, unchanged.
Private Function id(s as String) As String
return s
End Function
The function TrivialParser wraps the construction of the delegate, and returns it.
Private Function TrivialParser() As ShellOutputParser
return AddressOf id
End Function
I considered having this trivial parser trim trailing whitespace, but realized that was unnecessary, because I can simply pass the existing function RTrim.
Here is a final version of the code that gets the machine uptime.
dim uptime as new Date
dim s as new Shell
uptime.TotalSeconds = uptime.TotalSeconds - Val(s.Execute("cat /proc/uptime", AddressOf ParseUptime))
Finally, I was a little surprised that my code compiled, because the extension method overloads the existing method Shell.Execute, and uses a default parameter. My guess is that the presence of a return type means that the compiler finds no match when it first searches for a Shell method with the signature, then searches for extension methods and finds a match.
Were I feeling more cautious, I would change the name of the extension method. But it works now, and I like being able to use the same name as the existing method.
Commenting is closed for this article.