Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[host][freebsd] Users() returns incorrect "Started" timestamp #709

Closed
1 task done
ppmathis opened this issue Jun 30, 2019 · 5 comments
Closed
1 task done

[host][freebsd] Users() returns incorrect "Started" timestamp #709

ppmathis opened this issue Jun 30, 2019 · 5 comments

Comments

@ppmathis
Copy link

Describe the bug
Calling host.Users() on a FreeBSD system which uses the utmpx POSIX standard (default since FreeBSD 9.0) returns a slice of UserStat structs, which contains an incorrect value for the Started attribute. Instead of containing a UNIX timestamp representing the start time of the user session, a seemingly random timestamp is being returned.

To Reproduce
Start a new user session at a FreeBSD system (e.g. SSH to the machine), then run the following code snippet:

package main

import (
	"fmt"
	"github.com/shirou/gopsutil/host"
)

func main() {
	users, _ := host.Users()
	for _, user := range users {
		fmt.Printf("%s@%s@%s - %d", user.User, user.Terminal, user.Host, user.Started)
	}
}

As an example, this resulted in the following output on my system: root@pts/0@<my reverse dns> - 1732593154 - converting the supposed UNIX timestamp to a human-readable date results in 11/26/2024 @ 3:52am (UTC), which is obviously way off as I am unable to start sessions in the future :)

Expected behavior
Calling host.Users() on a FreeBSD system should return a correct UNIX timestamp specifying when the session got started. This behavior would be equivalent to the other supported operating systems, e.g. Linux.

Environment (please complete the following information):

  • FreeBSD: 12.0-RELEASE-p6 / FreeBSD gopsutil-devel 12.0-RELEASE-p6 FreeBSD 12.0-RELEASE-p6 GENERIC amd64

Root cause
gopsutil is currently using the C struct declaration utmpx, which is incompatible with the format used by FreeBSD for /var/run/utx.active. While struct utmpx is being represented according to the POSIX standards and used internally in the kernel, FreeBSD chose to use a different representation for the utx.active file.

This can be read in the initial commit which introduced the utmpx implementation to FreeBSD in 2010:

The standard allows the on-disk format to be different than the in-memory representation (struct utmpx). Most operating systems don't do this, but we do. This allows us to keep our ABI more stable, while giving us the opportunity to modify the on-disk format. It also allows us to use a common file format across different architectures (i.e. byte ordering).

The correct structure which should be used for parsing the file is present in lib/libc/gen/utxdb.h, which has some notable differences:

  • The structure is exactly 197 bytes long per user session, which also explains the currently present TODO tag in gopsutil/host/host_freebsd_amd64.go: // TODO: why should 197, not 0x118
  • The session type field fu_type is a uint8_t instead of a short, so only one byte is being used instead of two as currently implemented in gopsutil.
  • The timestamp field fu_tv is a uint64_t instead of a struct timeval, which consumes 8 bytes instead of 7 bytes.
  • The timestamp is implemented as secs * 1_000_000 + usecs, which can also be seen in the kernel sources. secs represents the UNIX timestamp and usecs gives additional precision.

I have already verified that using struct futx would result in the correct timestamp by analyzing a copy of utx.active in a hex editor, which can be seen here: https://zig.pw/3916bfaca6116fe1e5d67b314e6de03c.png

Unfortunately this structure is not being included in the regular header files available in a base FreeBSD installation, so this structure would either have to be manually replicated (without Cgo) or we would have to use the actual C functions to retrieve struct utmpx instances. I couldn't think of a satisfying solution so far, which is why I am opening this as an issue.

@shirou
Copy link
Owner

shirou commented Jul 1, 2019

Thank you! I will check in this weekend.

@Lomanic
Copy link
Collaborator

Lomanic commented Jul 2, 2019

Thanks for this excellent report. I can reproduce indeed. I tried to regenerate host_freebsd_amd64.go with cgo -godefs types_freebsd.go > host_freebsd_amd64.go on a FreeBSD 12.0 RELEASE VM, but then Utmp was defined as an empty struct and would break the code (and generated types would be sometimes different also).

@shirou
Copy link
Owner

shirou commented Jul 6, 2019

From this great report, I can create a fix PR #712. please check it and if it works on your environment, I can merge the PR. Thanks!

@ppmathis
Copy link
Author

ppmathis commented Jul 8, 2019

@shirou Thank you very much! I have commented on the PR regarding my test results and can confirm that it does fix this specific issue.

@shirou
Copy link
Owner

shirou commented Jul 9, 2019

#712 is merged. Big thanks for this report and your contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants