logo

utils

~/.local/bin tools and git-hooks git clone https://hacktivis.me/git/utils.git
commit: 2379db5af2efc295cc3f138c4c1cbc06ac082840
parent 0eb20a5bb5c4019d5dc8523e4e38833efd118fb8
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Sat, 12 Mar 2022 01:27:21 +0100

bin/id: Add

Diffstat:

Abin/id.c269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcoreutils.txt2+-
Mtest-bin/Kyuafile1+
Atest-bin/id56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 327 insertions(+), 1 deletion(-)

diff --git a/bin/id.c b/bin/id.c @@ -0,0 +1,269 @@ +// Collection of Unix tools, comparable to coreutils +// Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only + +#define _POSIX_C_SOURCE 200809L +#define _DEFAULT_SOURCE // for getgrouplist + +#include <assert.h> // assert +#include <grp.h> // getgrgid, getgrouplist +#include <pwd.h> // getpwuid +#include <stdbool.h> // bool +#include <stdio.h> // printf, perror +#include <stdlib.h> // malloc, free +#include <sys/types.h> // uid_t +#include <unistd.h> // getuid, getgid, getopt, opt* + +bool name_flag = false; + +static int +list_groups(struct passwd *pw) +{ + int ngroups = (int)sysconf(_SC_NGROUPS_MAX) + 1; + gid_t *groups = malloc(sizeof(gid_t) * ngroups); + if(groups == NULL) + { + perror("groups malloc"); + return 1; + } + + // getgrouplist(3) BSD extension might not be in all Unixes + int grls = getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + if(grls >= 0) + { + printf(" groups="); + + for(int i = 0; i < ngroups; i++) + { + struct group *lgr = getgrgid(groups[i]); + + if(name_flag) + { + if(lgr == NULL) + { + goto failure; + } + + int sep = ' '; + if(i == ngroups - 1) + { + sep = '\0'; + } + + int ret = printf("%s%c", lgr->gr_name, sep); + if(ret < 0) + { + goto failure; + } + } + else + { + int ret = printf("%d", groups[i]); + if(ret < 0) + { + goto failure; + } + + if(lgr != NULL) + { + int ret = printf("(%s)", lgr->gr_name); + if(ret < 0) + { + goto failure; + } + } + + if(i != ngroups - 1) + { + printf(","); + } + } + } + } + + free(groups); + return 0; + +failure: + free(groups); + return 1; +} + +int +print_gid(char *field, char *name, gid_t gid) +{ + if(name_flag) + { + if(name) + { + return printf("%s=%s", field, name); + } + else + { + return -1; + } + } + + if(name) + { + return printf("%s=%d(%s)", field, gid, name); + } + else + { + return printf("%s=%d", field, gid); + } +} + +int +print_uid(char *field, char *name, uid_t uid) +{ + if(name_flag) + { + if(name) + { + return printf("%s=%s", field, name); + } + else + { + return -1; + } + } + + if(name) + { + return printf("%s=%d(%s)", field, uid, name); + } + else + { + return printf("%s=%d", field, uid); + } +} + +int +main(int argc, char *argv[]) +{ + int ret, c; + bool euid_flag = false; + bool G_flag = false; + + /* flawfinder: ignore. Old implementations of getopt should fix themselves */ + while((c = getopt(argc, argv, ":Ggunr")) != EOF) + { + switch(c) + { + case 'G': + G_flag = true; + break; + case 'n': + name_flag = true; + break; + case 'u': + euid_flag = true; + break; + default: + assert(false); + } + } + + argc -= optind; + argv += optind; + + // geteuid, getuid, getegid, getgid shall always be successful + uid_t uid = getuid(); + uid_t euid = geteuid(); + gid_t gid = getgid(); + gid_t egid = getegid(); + + struct passwd *pw = getpwuid(uid); + struct passwd *epw = getpwuid(euid); + // can return NULL (ie. without /etc/passwd) + + if(pw != NULL) + { + assert(pw->pw_uid == uid); + + ret = print_uid("uid", pw->pw_name, uid); + } + else + { + ret = print_uid("uid", NULL, uid); + } + + if(ret < 0) + { + return 1; + } + + if(euid != uid) + { + if(epw != NULL) + { + assert(epw->pw_uid == euid); + + ret = print_uid(" euid", epw->pw_name, euid); + } + else + { + ret = print_uid(" euid", NULL, euid); + } + + if(ret < 0) + { + return 1; + } + } + + struct group *gr = getgrgid(gid); + + if(gr != NULL) + { + assert(gr->gr_gid == gid); + + ret = print_gid(" gid", gr->gr_name, gid); + } + else + { + ret = print_gid(" gid", NULL, gid); + } + + if(ret < 0) + { + return 1; + } + + struct group *egr = getgrgid(egid); + if(egid != gid) + { + if(egr != NULL) + { + assert(egr->gr_gid == egid); + + ret = print_gid(" egid", egr->gr_name, egid); + } + else + { + ret = print_gid(" egid", NULL, egid); + } + + if(ret < 0) + { + return 1; + } + } + + if(pw != NULL) + { + if(list_groups(pw) != 0) + { + return 1; + } + } + + ret = printf("\n"); + if(ret < 0) + { + return 1; + } + + return 0; +} diff --git a/coreutils.txt b/coreutils.txt @@ -33,7 +33,7 @@ fmt: ? fold: ? head: No, use sed hostid: No, get a better id(ea) -id: Todo +id: Done install: No? join: ? link: Todo diff --git a/test-bin/Kyuafile b/test-bin/Kyuafile @@ -16,6 +16,7 @@ atf_test_program{name="env", required_files=basedir.."/bin/env"} atf_test_program{name="errno", required_files=basedir.."/bin/errno"} atf_test_program{name="false", required_files=basedir.."/bin/false"} atf_test_program{name="humanize", required_files=basedir.."/bin/humanize"} +atf_test_program{name="id", required_files=basedir.."/bin/id"} atf_test_program{name="mdate", required_files=basedir.."/bin/mdate"} atf_test_program{name="pwd", required_files=basedir.."/bin/pwd"} atf_test_program{name="seq", required_files=basedir.."/bin/seq"} diff --git a/test-bin/id b/test-bin/id @@ -0,0 +1,56 @@ +#!/usr/bin/env atf-sh +atf_test_case noargs cleanup +noargs_body() { + atf_check -o save:noargs.out ../bin/id + atf_check grep -q "uid=$(id -u)($(id -un)) gid=$(id -g)($(id -gn)) groups=" noargs.out +} +noargs_cleanup() { + rm -f noargs.out +} + +atf_test_case names cleanup +names_body() { + atf_check -o save:names.out ../bin/id -n + atf_check grep -q "uid=$(id -un) gid=$(id -gn) groups=" names.out +} +names_cleanup() { + rm -f names.out +} + +atf_test_case devfull +devfull_body() { + has_glibc && atf_expect_fail "glibc ignoring write errors for puts()" + [ "$(uname -s)" = "NetBSD" ] && atf_expect_fail "NetBSD ignoring write errors for puts()" + [ "$(uname -s)" = "FreeBSD" ] && atf_expect_fail "FreeBSD ignoring write errors for puts()" + + atf_check -s exit:1 sh -c '../bin/id >/dev/full' + atf_check -s exit:1 sh -c '../bin/id -n >/dev/full' +} + +atf_test_case noetc +noetc_body() { + bwrap_args="--ro-bind-try /usr /usr --ro-bind-try /lib /lib --ro-bind-try /lib64 /lib64 --ro-bind ../bin /bin" + + command -v bwrap >/dev/null 2>/dev/null || atf_skip "'bwrap' command not found" + + set -f + + # shellcheck disable=SC2086 + atf_check -o "inline:uid=$(id -u) gid=$(id -g)\n" -x -- bwrap ${bwrap_args} /bin/id + + # shellcheck disable=SC2086 + atf_check -s exit:1 -x -- bwrap ${bwrap_args} /bin/id -n +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + . ../test_functions.sh + + atf_add_test_case devfull + + atf_add_test_case noargs + atf_add_test_case names + + atf_add_test_case noetc +}