{"id":28958768,"url":"https://github.com/ericwq/goutmp","last_synced_at":"2025-06-23T23:07:06.211Z","repository":{"id":167807061,"uuid":"643434629","full_name":"ericwq/goutmp","owner":"ericwq","description":"golang client which support utmpx API","archived":false,"fork":false,"pushed_at":"2024-06-25T07:22:23.000Z","size":65,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-06-25T09:04:27.029Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ericwq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-21T06:29:46.000Z","updated_at":"2024-06-25T07:22:24.000Z","dependencies_parsed_at":"2024-03-17T23:26:02.829Z","dependency_job_id":"5e621051-618e-4a24-9337-96ad9d4c8ce6","html_url":"https://github.com/ericwq/goutmp","commit_stats":null,"previous_names":["ericwq/goutmp"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/ericwq/goutmp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericwq%2Fgoutmp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericwq%2Fgoutmp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericwq%2Fgoutmp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericwq%2Fgoutmp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericwq","download_url":"https://codeload.github.com/ericwq/goutmp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericwq%2Fgoutmp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261571672,"owners_count":23178769,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-23T23:07:05.418Z","updated_at":"2025-06-23T23:07:06.185Z","avatar_url":"https://github.com/ericwq.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# goutmp\n\nThis is a golang client module which support `utmpx` API: which includes [utmpx](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/utmpx.h.html) and [wtmp](https://man7.org/linux/man-pages/man3/updwtmpx.3.html). The new API is inspired by [libutempter](https://manpages.ubuntu.com/manpages/lunar/en/man3/utempter.3.html). Currently, The implementation is a golang wrapper for `utmpx` C library. On alpine linux (musl based) it will call [utmps](https://skarnet.org/software/utmps/) through utmpx API. On other linux (glibc based) it will call glibc utmpx API.\n\nTo use goutmp module in your golang development, you need to provide additional build tags.\n\n```sh\ngo build -tags utmps . # for alpine linux\ngo build -tags utmp .  # for glibc based linux\n```\n\n## API\n\n```go\n// called when user login, adds a login utmp/wtmp record to database with the specified\n// pseudo-terminal device name, user name, host name and process PID.\n// this function will update both utmp and wtmp within one call\nfunc AddRecord(ptsName string, user string, host string, pid int) bool {\n\n// called when user logout, marks a login session as being closed with the specified\n// pseudo-terminal device name, process PID.\n// this function will update both utmp and wtmp within one call\nfunc RemoveRecord(ptsName string, pid int) bool {\n\n// read the next utmpx record from utmp database\nfunc GetRecord() *Utmpx\n\n// Add a record to last log, with the specified login line, username and\n// originating host/IP.\nfunc AddLastLog(line, userName, host string) bool {\n\n```\n## Edit in nvim\nTo edit or browse source code in this project, you need to set the following environment variable for [nvide](../nvide/README.md)\n```sh\nexport GOFLAGS=\"-tags=utmps\"    # for musl based linux, such as: alpine\nexport GOFLAGS=\"-tags=utmp\"     # for glibc based linux, such as: fedora, redhat\n```\n\n## Prepare the utmps environment\n\nRefer to [alpine container with openrc support](https://github.com/ericwq/s6) to build the docker image, run the following command to start container.\n```sh\n% docker run --env TZ=Asia/Shanghai --tty --privileged --volume /sys/fs/cgroup:/sys/fs/cgroup:ro \\\n  -h openrc-ssh --name openrc-ssh -d -p 5022:22 openrc-ssh:0.1.0\n```\n\nInstall `go` SDK and `utmps-dev` package to prepare build dependencies. Note, the following command require root privilege.\n```sh\n# apk add go utmps-dev\n```\n\nRun `setup-utmp` script to setup `utmps` services for the container. Note, this command require root privilege.\n```sh\n# setup-utmp\n```\n\nRestart the container. Run the following command to make sure everything works. `pstree` command shows that 3 `utmps` related service is ready for us. `who` and `last` command shows the correct result from `utmps` service.\n\n```sh\nopenrc-ssh:~# pstree -p\ninit(1)-+-s6-ipcserverd(154)\n        |-s6-ipcserverd(217)\n        |-s6-ipcserverd(245)\n        `-sshd(190)---sshd(286)---ash(288)---pstree(292)\nopenrc-ssh:~# who\nroot            pts/1           00:00   May 29 22:48:09  172.17.0.1\nopenrc-ssh:~# last\nUSER       TTY            HOST               LOGIN        TIME\nroot       pts/1          172.17.0.1         May 29 22:48\n```\n\n## Build and run goutmp application.\n\nNow, it's time to build your application and run it according to the following section.\n\nAdd user `ide` into `utmp` group. This is required by `utmps`. Note, this command require root privilege.\n\n```sh\nopenrc-ssh:~# adduser ide utmp\n```\n\nSet GID for the application. Note, local mounted file system in docker (such as the ~/develop directory) doesn't support set GID application. That is why we move it to the `/tmp` directory.\n\n```sh\nopenrc-ssh:~/develop/goutmp$ go build -tags utmps main/test_linux.go\nopenrc-ssh:~/develop/goutmp$ ls -al\ntotal 2116\ndrwxr-xr-x   11 ide      develop        352 May 29 22:50 .\ndrwxr-xr-x   24 ide      develop        768 May 24 07:00 ..\ndrwxr-xr-x   16 ide      develop        512 May 29 21:59 .git\n-rw-r--r--    1 ide      develop        515 May 28 19:16 .gitignore\n-rw-r--r--    1 ide      develop       1063 May 21 14:31 LICENSE\n-rw-r--r--    1 ide      develop       4651 May 29 22:49 README.md\n-rw-r--r--    1 ide      develop         41 May 21 14:34 go.mod\n-rw-r--r--    1 ide      develop       4120 May 28 19:13 goutmp_linux.go\ndrwxr-xr-x    3 ide      develop         96 May 28 20:11 main\n-rwxr-xr-x    1 ide      develop    2137992 May 29 22:50 test_linux\ndrwxr-xr-x    6 ide      develop        192 May 28 19:13 xutmp\nopenrc-ssh:~/develop/goutmp$ cp test_linux  /tmp/\nopenrc-ssh:~/develop/goutmp$ cd /tmp\nopenrc-ssh:/tmp$ chgrp utmp test_linux  # run as root\nopenrc-ssh:/tmp$ chmod g+s test_linux   # run as root\nopenrc-ssh:/tmp$ ls -al\ntotal 2096\ndrwxrwxrwt    1 root     root          4096 May 29 22:50 .\ndrwxr-xr-x    1 root     root          4096 May 29 22:41 ..\n-rwxr-sr-x    1 ide      utmp       2137992 May 29 22:50 test_linux\nopenrc-ssh:/tmp$ ./test_linux\n```\n\n## How to set effective GID for your service\nYou has a service and want that service has the privileges to access `utmps` service. Then you need to set the effective GID for your service to be `utmp`. The `utmps` service require effective GID of `utmp`. Refer to [The utmps-utmpd program](https://skarnet.org/software/utmps/utmps-utmpd.html) for detail.\n\nLet's say your service program is `prog2`. You need set GID for `prog2`. Let's assume that user `ide` belongs to two groups: `develop` and `utmp`. You can use `$ adduser ide utmp` command to achieve it.\n\n- first, change the group of `prog2` to `utmp`.\n- second, set-GID for `prog2`.\n- finally, if you run the `prog2` program, it's effective GID will be `utmp`.\n\n```sh\nopenrc-ssh:/tmp$ ls -al\ntotal 2096\ndrwxrwxrwt    1 root     root          4096 May 27 20:38 .\ndrwxr-xr-x    1 root     root          4096 May 27 18:12 ..\n-rwxr-xr-x    1 ide      develop    2137512 May 27 20:38 prog2\nopenrc-ssh:/tmp$ chgrp utmp prog2\nopenrc-ssh:/tmp$ ls -al\ntotal 2096\ndrwxrwxrwt    1 root     root          4096 May 27 20:38 .\ndrwxr-xr-x    1 root     root          4096 May 27 18:12 ..\n-rwxr-xr-x    1 ide      utmp       2137512 May 27 20:38 prog2\nopenrc-ssh:/tmp$ chmod g+s prog2\nopenrc-ssh:/tmp$ ls -al\ntotal 2096\ndrwxrwxrwt    1 root     root          4096 May 27 20:38 .\ndrwxr-xr-x    1 root     root          4096 May 27 18:12 ..\n-rwxr-sr-x    1 ide      utmp       2137512 May 27 20:38 prog2\n```\n\nPlease refer to [s6-setuidgid](https://skarnet.org/software/s6/s6-setuidgid.html) to accomplish the above work in a single command. Note: in docker environment, mounted local file system does not support set UID/GID operation.\n\n## Difference with original goutmp\n\nAfter search the internet, I found [RLabs/goutmp](https://gogs.blitter.com/RLabs/goutmp) and decide to use it to access `utmp` and `wutmp` database. As I learn more about utmp/utmpx API and `RLabs/goutmp`. I found it's time to create an alternative go module.\n\nThere are several differences between `ericwq/goutmp` and `RLabs/goutmp`. `ericwq/goutmp` refer to `libutempter` and the example from [The linux programming interface](https://www.oreilly.com/library/view/the-linux-programming/9781593272203/) (P829).\n\n- `ericwq/goutmp` support `utmpx` API, while `RLabs/goutmp` support `utmp` API.\n- `ericwq/goutmp` update `wtmp` when update `utmp` record. This behavior is more reasonable.\n- `ericwq/goutmp` support `tty` and `pts` device, while `RLabs/goutmp` only support `pts` device.\n\n## Inline C or stand alone C module\nWe use the following cgo directive and inline C functions to implement the wrapper. Inline C functions is more easy to build than stand alone C module.\n```c\n// #cgo pkg-config: utmps skalibs\n```\n\nPlease use the following commands to build the stand alone C module, either staticly or dynamicly.\n```sh\n$ cd ./xutmp/\n$ gcc -I/usr/include/utmps -lutmps -lskarnet -c -o xutmp.o xutmp.c\n$ ar rcs libxutmp.a xutmp.o\n```\n\n```sh\n$ gcc -shared -I/usr/include/utmps -lutmps -lskarnet -o libxutmp.so xutmp.c\n```\n\nThe following cgo directive is for stand alone C module.\n```c\n/*\n#cgo CFLAGS: -I./xutmp\n#cgo LDFLAGS: -L${SRCDIR}/xutmp -lxutmp\n\n#include \"xutmp.h\"\n*/\n```\n## License\n\nIt's MIT license, please see the LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericwq%2Fgoutmp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericwq%2Fgoutmp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericwq%2Fgoutmp/lists"}