From 8f8ead93f1b42e673db33ffde3d40128e8502162 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 27 Jun 2015 03:12:26 +0200 Subject: [PATCH] Add id(1). --- doc/user-guide | 1 + utils/.gitignore | 1 + utils/Makefile | 1 + utils/id.cpp | 240 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 utils/id.cpp diff --git a/doc/user-guide b/doc/user-guide index 32120ede..d37f30df 100644 --- a/doc/user-guide +++ b/doc/user-guide @@ -173,6 +173,7 @@ Sortix comes with a number of home-made programs. Here is an overview: * `find` - recursively list files * `head` - display start of file * `help` - show list of available programs +* `id` - print real and effective user and group IDs * `init` - system management deamon * `initrdfs` - tool for examining initrds * `install` - installs a program into a system directory diff --git a/utils/.gitignore b/utils/.gitignore index 7ae4fc33..89b3413a 100644 --- a/utils/.gitignore +++ b/utils/.gitignore @@ -18,6 +18,7 @@ expr false find head +id help kernelinfo kill diff --git a/utils/Makefile b/utils/Makefile index fa09df33..6668cb09 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -31,6 +31,7 @@ false \ find \ head \ help \ +id \ kernelinfo \ kill \ ln \ diff --git a/utils/id.cpp b/utils/id.cpp new file mode 100644 index 00000000..119f333e --- /dev/null +++ b/utils/id.cpp @@ -0,0 +1,240 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2015. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . + + id.cpp + Return user identity. + +*******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char* name_of_group_by_gid(gid_t gid) +{ + errno = 0; + struct group* grp = getgrgid(gid); + if ( !grp && errno == 0 ) + errx(1, "gid %" PRIuGID " has no group entry", gid); + if ( !grp ) + err(1, "gid %" PRIuGID "", gid); + return grp->gr_name; +} + +static void compact_arguments(int* argc, char*** argv) +{ + for ( int i = 0; i < *argc; i++ ) + { + while ( i < *argc && !(*argv)[i] ) + { + for ( int n = i; n < *argc; n++ ) + (*argv)[n] = (*argv)[n+1]; + (*argc)--; + } + } +} + +static void help(FILE* fp, const char* argv0) +{ + fprintf(fp, "Usage: %s [OPTION]... [USERNAME]\n", argv0); + fprintf(fp, "Print user and group information for the specified USERNAME,\n"); + fprintf(fp, "or (when USERNAME omitted) for the current user.\n"); + fprintf(fp, "\n"); + fprintf(fp, " -g, --group print only the effective group ID\n"); + fprintf(fp, " -G, --groups print all group IDs\n"); + fprintf(fp, " -n, --name print a name instead of a number, for -ugG\n"); + fprintf(fp, " -r, --real print the real ID instead of the effective ID, with -ugG\n"); + fprintf(fp, " -u, --user print only the effective user ID\n"); + fprintf(fp, " --help display this help and exit\n"); + fprintf(fp, " --version output version information and exit\n"); +} + +static void version(FILE* fp, const char* argv0) +{ + fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR); + fprintf(fp, "License GPLv3+: GNU GPL version 3 or later .\n"); + fprintf(fp, "This is free software: you are free to change and redistribute it.\n"); + fprintf(fp, "There is NO WARRANTY, to the extent permitted by law.\n"); +} + +int main(int argc, char* argv[]) +{ + setlocale(LC_ALL, ""); + + bool groups_only = false; + bool group_only = false; + bool user_only = false; + bool names = false; + bool real = false; + + const char* argv0 = argv[0]; + for ( int i = 1; i < argc; i++ ) + { + const char* arg = argv[i]; + if ( arg[0] != '-' || !arg[1] ) + continue; + argv[i] = NULL; + if ( !strcmp(arg, "--") ) + break; + if ( arg[1] != '-' ) + { + while ( char c = *++arg ) switch ( c ) + { + case 'G': groups_only = true; break; + case 'g': group_only = true; break; + case 'n': names = true; break; + case 'r': real = true; break; + case 'u': user_only = true; break; + default: + fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c); + help(stderr, argv0); + exit(1); + } + } + else if ( !strcmp(arg, "--group") ) + group_only = true; + else if ( !strcmp(arg, "--groups") ) + groups_only = true; + else if ( !strcmp(arg, "--name") ) + names = true; + else if ( !strcmp(arg, "--real") ) + real = true; + else if ( !strcmp(arg, "--user") ) + user_only = true; + else if ( !strcmp(arg, "--help") ) + help(stdout, argv0), exit(0); + else if ( !strcmp(arg, "--version") ) + version(stdout, argv0), exit(0); + else + { + fprintf(stderr, "%s: unknown option: %s\n", argv0, arg); + help(stderr, argv0); + exit(1); + } + } + + compact_arguments(&argc, &argv); + + if ( 1 < groups_only + group_only + user_only ) + errx(1, "options -Ggu are mutually exclusive"); + + (void) real; // Sortix only has real user ids. + + struct passwd* pwd; + if ( 2 < argc ) + errx(1, "extra operand '%s'", argv[2]); + else if ( argc == 2 ) + { + errno = 0; + pwd = getpwnam(argv[1]); + if ( !pwd && errno == 0 ) + errx(1, "%s: no such user", argv[1]); + if ( !pwd ) + err(1, "%s", argv[1]); + } + else + { + errno = 0; + pwd = getpwuid(getuid()); + if ( !pwd && errno == 0 ) + errx(1, "current user has no user entry"); + if ( !pwd ) + err(1, "current user"); + } + + if ( groups_only ) + { + // TODO: Sortix doesn't have getgroups at the moment. + if ( names ) + printf("%s", name_of_group_by_gid(pwd->pw_gid)); + else + printf("%" __PRIuGID, pwd->pw_gid); + setgrent(); + struct group* grp; + while ( (errno = 0, grp = getgrent()) ) + { + if ( grp->gr_gid == pwd->pw_gid ) + continue; + bool member = false; + for ( size_t i = 0; !member && grp->gr_mem[i]; i++ ) + member = !strcmp(pwd->pw_name, grp->gr_mem[i]); + if ( !member ) + continue; + if ( names ) + printf(" %s", grp->gr_name); + else + printf(" %" __PRIuGID "", grp->gr_gid); + } + if ( errno != 0 ) + err(1, "getgrent"); + endgrent(); + printf("\n"); + } + else if ( group_only ) + { + // If current user, should this display the getgid() group instead? + if ( names ) + printf("%s\n", name_of_group_by_gid(pwd->pw_gid)); + else + printf("%" __PRIuGID "\n", pwd->pw_gid); + } + else if ( user_only ) + { + if ( names ) + printf("%s\n", pwd->pw_name); + else + printf("%" __PRIuUID "\n", pwd->pw_uid); + } + else + { + if ( names || real ) + errx(1, "cannot print only names or real IDs in default format"); + name_of_group_by_gid(pwd->pw_gid); // Early error. + printf("uid=%" PRIuUID "(%s) ", pwd->pw_uid, pwd->pw_name); + printf("gid=%" PRIiGID "(%s) ", pwd->pw_gid, name_of_group_by_gid(pwd->pw_gid)); + printf("groups="); + const char* prefix = ""; + setgrent(); + struct group* grp; + while ( (errno = 0, grp = getgrent()) ) + { + bool member = false; + for ( size_t i = 0; !member && grp->gr_mem[i]; i++ ) + member = !strcmp(pwd->pw_name, grp->gr_mem[i]); + if ( !member ) + continue; + printf("%s%" __PRIuGID "(%s)", prefix, grp->gr_gid, grp->gr_name); + prefix = ","; + } + if ( errno != 0 ) + err(1, "getgrent"); + endgrent(); + printf("\n"); + } + + if ( ferror(stdout) || fflush(stdout) == EOF ) + return 1; + return 0; +}