commit ef0c281b86c873957449b05a0304cd9b41eceea2
parent 7d4af1172ba0fa65d3307e160ad76c1ca1287df6
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 28 Aug 2022 11:22:33 +0200
unix::passwd: add get*id, improve *_finish
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
2 files changed, 75 insertions(+), 14 deletions(-)
diff --git a/unix/passwd/group.ha b/unix/passwd/group.ha
@@ -21,7 +21,7 @@ export type grent = struct {
};
// Reads a Unix-like group entry from an [[io::handle]]. The caller must free
-// the result using [[grent_finish]].
+// the return value using [[grent_finish]].
export fn nextgr(in: io::handle) (grent | io::EOF | io::error | invalid) = {
let line = match (bufio::scanline(in)?) {
case let ln: []u8 =>
@@ -60,7 +60,7 @@ export fn nextgr(in: io::handle) (grent | io::EOF | io::error | invalid) = {
};
// Frees resources associated with [[grent]].
-export fn grent_finish(ent: grent) void = {
+export fn grent_finish(ent: *grent) void = {
free(ent.name);
free(ent.userlist);
};
@@ -91,11 +91,40 @@ export fn getgroup(name: str) (grent | void) = {
if (ent.name == name) {
return ent;
} else {
- grent_finish(ent);
+ grent_finish(&ent);
};
};
+};
+
+// Looks up a group by ID in a Unix-like group file. It expects a such file at
+// /etc/group. Aborts if that file doesn't exist or is not properly formatted.
+//
+// See [[nextgr]] for low-level parsing API.
+export fn getgid(gid: uint) (grent | void) = {
+ let file = match (os::open("/etc/group")) {
+ case let f: io::file =>
+ yield f;
+ case =>
+ abort("Unable to open /etc/group");
+ };
+ defer io::close(file)!;
+
+ for (true) {
+ let ent = match (nextgr(file)) {
+ case let e: grent =>
+ yield e;
+ case io::EOF =>
+ break;
+ case =>
+ abort("Invalid entry in /etc/group");
+ };
- return;
+ if (ent.gid == gid) {
+ return ent;
+ } else {
+ grent_finish(&ent);
+ };
+ };
};
@test fn nextgr() void = {
@@ -105,7 +134,7 @@ export fn getgroup(name: str) (grent | void) = {
"video:x:986:alex,wmuser"), io::mode::READ);
let ent = nextgr(&buf) as grent;
- defer grent_finish(ent);
+ defer grent_finish(&ent);
assert(ent.name == "root");
assert(ent.password == "x");
@@ -114,7 +143,7 @@ export fn getgroup(name: str) (grent | void) = {
assert(ent.userlist[0] == "root");
let ent = nextgr(&buf) as grent;
- defer grent_finish(ent);
+ defer grent_finish(&ent);
assert(ent.name == "mail");
assert(ent.password == "x");
@@ -122,7 +151,7 @@ export fn getgroup(name: str) (grent | void) = {
assert(len(ent.userlist) == 0);
let ent = nextgr(&buf) as grent;
- defer grent_finish(ent);
+ defer grent_finish(&ent);
assert(ent.name == "video");
assert(ent.password == "x");
diff --git a/unix/passwd/passwd.ha b/unix/passwd/passwd.ha
@@ -27,7 +27,7 @@ export type pwent = struct {
};
// Reads a Unix-like password entry from an [[io::handle]]. The caller must free
-// the result using [[pwent_finish]].
+// the return value using [[pwent_finish]].
export fn nextpw(file: io::handle) (pwent | io::EOF | io::error | invalid) = {
let line = match (bufio::scanline(file)?) {
case io::EOF =>
@@ -75,8 +75,8 @@ export fn nextpw(file: io::handle) (pwent | io::EOF | io::error | invalid) = {
};
};
-// Frees resources associated with [[pwent]].
-export fn pwent_finish(ent: pwent) void = {
+// Frees resources associated with a [[pwent]].
+export fn pwent_finish(ent: *pwent) void = {
// pwent fields are sliced from one allocated string returned by
// bufio::scanline. Freeing the first field frees the entire string in
// one go.
@@ -85,7 +85,7 @@ export fn pwent_finish(ent: pwent) void = {
// Looks up a user by name in a Unix-like password file. It expects a password
// database file at /etc/passwd. Aborts if that file doesn't exist or is not
-// properly formatted.
+// properly formatted. The return value must be freed with [[pwent_finish]].
//
// See [[nextpw]] for low-level parsing API.
export fn getuser(username: str) (pwent | void) = {
@@ -110,20 +110,52 @@ export fn getuser(username: str) (pwent | void) = {
if (ent.username == username) {
return ent;
} else {
- pwent_finish(ent);
+ pwent_finish(&ent);
};
};
return;
};
+// Looks up a user by ID in a Unix-like password file. It expects a password
+// database file at /etc/passwd. Aborts if that file doesn't exist or is not
+// properly formatted. The return value must be freed with [[pwent_finish]].
+//
+// See [[nextpw]] for low-level parsing API.
+export fn getuid(uid: uint) (pwent | void) = {
+ let file = match (os::open("/etc/passwd")) {
+ case let f: io::file =>
+ yield f;
+ case =>
+ abort("Can't open /etc/passwd");
+ };
+ defer io::close(file)!;
+
+ for (true) {
+ let ent = match (nextpw(file)) {
+ case let e: pwent =>
+ yield e;
+ case io::EOF =>
+ break;
+ case =>
+ abort("Invalid entry in /etc/passwd");
+ };
+
+ if (ent.uid == uid) {
+ return ent;
+ } else {
+ pwent_finish(&ent);
+ };
+ };
+};
+
@test fn nextpw() void = {
let buf = bufio::fixed(strings::toutf8(
"sircmpwn:x:1000:1000:sircmpwn's comment:/home/sircmpwn:/bin/mrsh\n"
"alex:x:1001:1001::/home/alex:/bin/zsh"), io::mode::READ);
let ent = nextpw(&buf) as pwent;
- defer pwent_finish(ent);
+ defer pwent_finish(&ent);
assert(ent.username == "sircmpwn");
assert(ent.password == "x");
@@ -134,7 +166,7 @@ export fn getuser(username: str) (pwent | void) = {
assert(ent.shell == "/bin/mrsh");
let ent = nextpw(&buf) as pwent;
- defer pwent_finish(ent);
+ defer pwent_finish(&ent);
assert(ent.username == "alex");
assert(ent.password == "x");