This commit is contained in:
ge
2026-01-25 13:31:10 +03:00
commit 3b381ba118
10 changed files with 303 additions and 0 deletions

8
.editorconfig Normal file
View File

@@ -0,0 +1,8 @@
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.v]
indent_style = tab

8
.gitattributes vendored Normal file
View File

@@ -0,0 +1,8 @@
* text=auto eol=lf
*.bat eol=crlf
*.v linguist-language=V
*.vv linguist-language=V
*.vsh linguist-language=V
v.mod linguist-language=V
.vdocignore linguist-language=ignore

48
.github/workflows/docs.yaml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Docs
on:
push:
branches: [ "master" ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup V
run: |
wget -qO /tmp/v.zip https://github.com/vlang/v/releases/latest/download/v_linux.zip
unzip -q /tmp/v.zip -d /tmp
echo /tmp/v >> "$GITHUB_PATH"
- name: Build docs
run: |
v doc -f html -m .
pushd _docs
ln -vs ${{ github.event.repository.name }}.html index.html
ls -alFh
popd
- name: Upload static files as artifact
id: deployment
uses: actions/upload-pages-artifact@v3
with:
path: _docs/
deploy:
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
permissions:
contents: read
pages: write
id-token: write

24
.github/workflows/test.yaml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Tests
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup V
run: |
wget -qO /tmp/v.zip https://github.com/vlang/v/releases/latest/download/v_linux.zip
unzip -q /tmp/v.zip -d /tmp
echo /tmp/v >> "$GITHUB_PATH"
- name: Run tests
run: |
v -stats test .

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Binaries for programs and plugins
main
pwd
*.exe
*.exe~
*.so
*.dylib
*.dll
# Ignore binary output folders
bin/
# Ignore common editor/system specific metadata
.DS_Store
.idea/
.vscode/
*.iml
# ENV
.env
# vweb and database
*.db
*.js

8
README.md Normal file
View File

@@ -0,0 +1,8 @@
# Access to the UNIX Password Database
`pwd` module provides thread-safe access to the UNIX user account and password
database.
See [passwd(5)](https://man7.org/linux/man-pages/man5/passwd.5.html),
[getpwent(3)](https://man7.org/linux/man-pages/man3/getpwent.3.html) and
[getpwnam(3)](https://man7.org/linux/man-pages/man3/getpwnam.3.html) for info.

22
UNLICENSE Normal file
View File

@@ -0,0 +1,22 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and
successors. We intend this dedication to be an overt act of relinquishment in
perpetuity of all present and future rights to this software under copyright
law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

137
pwd.c.v Normal file
View File

@@ -0,0 +1,137 @@
@[has_globals]
module pwd
import sync
$if windows {
$compile_error('pwd: MS Windows is not supported')
}
#include <errno.h>
#include <pwd.h>
__global pwd_mutex &sync.Mutex
fn init() {
pwd_mutex = sync.new_mutex()
}
fn cleanup() {
pwd_mutex.destroy()
unsafe { free(pwd_mutex) }
}
struct C.passwd {
pw_name &char
pw_passwd &char
pw_uid i32
pw_gid i32
pw_gecos &char
pw_dir &char
pw_shell &char
}
fn C.getpwuid(i32) &C.passwd
fn C.getpwnam(&char) &C.passwd
fn C.setpwent()
fn C.endpwent()
fn C.getpwent() &C.passwd
pub struct Passwd {
pub:
name string // username
passwd string // user password, usually set to 'x'
uid int // used ID
gid int // group ID
gecos string // user information
dir string // home directory
shell string // shell program
}
fn make_passwd(pw &C.passwd) Passwd {
return unsafe {
Passwd{
name: cstring_to_vstring(pw.pw_name)
passwd: cstring_to_vstring(pw.pw_passwd)
uid: int(pw.pw_uid)
gid: int(pw.pw_gid)
gecos: cstring_to_vstring(pw.pw_gecos)
dir: cstring_to_vstring(pw.pw_dir)
shell: cstring_to_vstring(pw.pw_shell)
}
}
}
fn make_error(name string, uid int) IError {
err := C.errno
if err !in [4, 5, 12, 23, 24, 34] {
// Error is not EINTR, EIO, ENOMEM, ENFILE, EMFILE or ERANGE
return EntryNotFoundError{
name: name
uid: uid
}
}
return error_with_code(c_error_number_str(err), err)
}
// get_by_uid returns the passwd database entry by user ID.
// If the entry is not found, the EntryNotFoundError error will be returned.
pub fn get_by_uid(uid int) !Passwd {
pwd_mutex.lock()
defer {
pwd_mutex.unlock()
}
pw := C.getpwuid(uid)
if isnil(pw) {
return make_error('', uid)
}
return make_passwd(pw)
}
// get_by_uid returns the passwd database entry by user name.
// If the entry is not found, the EntryNotFoundError error will be returned.
pub fn get_by_name(name string) !Passwd {
pwd_mutex.lock()
defer {
pwd_mutex.unlock()
}
pw := C.getpwnam(&char(name.str))
if isnil(pw) {
return make_error(name, -1)
}
return make_passwd(pw)
}
// get_all returns all entries from passwd database in arbitrary order.
pub fn get_all() []Passwd {
mut pwds := []Passwd{}
pwd_mutex.lock()
C.setpwent()
defer {
C.endpwent()
pwd_mutex.unlock()
}
for {
pw := C.getpwent()
if isnil(pw) {
break
}
pwds << make_passwd(pw)
}
return pwds
}
// EntryNotFoundError designates that an entry with the specified UID or name was not found.
pub struct EntryNotFoundError {
Error
name string
uid int
}
// msg returns the string representation of EntryNotFoundError error.
pub fn (e EntryNotFoundError) msg() string {
if e.name != '' {
return "no passwd entry found by username '${e.name}'"
}
return 'no passwd entry found by user ID ${e.uid}'
}

17
pwd_test.v Normal file
View File

@@ -0,0 +1,17 @@
// vtest build: !windows
import pwd
fn test_get_by_uid() {
pw := pwd.get_by_uid(0)!
assert pw.name == 'root'
}
fn test_get_by_name() {
pw := pwd.get_by_name('root')!
assert pw.uid == 0
}
fn test_get_all() {
pws := pwd.get_all()
assert pws.len > 0
}

7
v.mod Normal file
View File

@@ -0,0 +1,7 @@
Module {
name: 'pwd'
description: 'Access to the UNIX password database'
version: '0.1.0'
license: 'Unlicense'
dependencies: []
}