Date: Mon, 27 May 1996 14:04:28 -0700 (MST) From: Terry Lambert <terry@lambert.org> To: regnauld@tetard.hsc.fr (Philippe Regnauld) Cc: hackers@freebsd.org Subject: Re: Adduser program in C Message-ID: <199605272104.OAA08998@phaeton.artisoft.com> In-Reply-To: <199605251733.TAA07641@tetard.hsc.fr> from "Philippe Regnauld" at May 25, 96 07:33:57 pm
next in thread | previous in thread | raw e-mail | index | archive | help
> PS: I have in mind creating a series of X admin tools with Perl/Tk > for FreeBSD. The main advantages are: easy to maintain, and easy > to adapt to other Unices (i.e.: Linux, etc.) Please do not do this. Try to think, instead, of a gramatical UI construct that can be used to build a (G)UI for any program meeting certain criteria. Specifically, it's easy to envision a user administration program that can operate in three modes: 1) Command line: % uadmin show user terry verbose NAME 'Terry Lambert' USER terry ID 501 GROUP 20 (staff) +GROUP 0 (wheel) +GROUP 552 (ncvs) HOME /home/terry SHELL /bin/csh EXPIRE PASSWORD NEVER EXPIRE ACCOUNT NEVER % 2) Interactive: % uadmin UADMIN> show user terry NAME 'Terry Lambert' USER terry ID 501 GROUP 20 (staff) HOME /home/terry SHELL /bin/csh EXPIRE PASSWORD NEVER EXPIRE ACCOUNT NEVER UADMIN> EXIT % 3) Subprocess of GUI via IPC: ,--------------------------------. | SHOW USER | ,--------------------------------. | | | Username: ____________________ | | | | | | > OK < >Cancel< >Browse...< | `--------------------------------' Clearly, 1) arguments invoke as command line 2) no arguments invoke as interarctive or UI driven a) interactive if stdin stats as tty b) UI driven if stdin stats as non-tty This implies a command parse hierarchy for the CLI and command line versions of the program: 1.0 Abstract The uadmin program implements a user administration tool. The uadmin tool is a CLI (command line interface) program which may be used to administer: o allowable shells o allowable groups o user accounts, including o user ID o login group o additional group memebrship o passwords, including o format requirements o password aging o defaults for the uadmin program, including o skeleton file directory o use of skeleton files o password assignment o default shell for new users o default group for new users o default minimum user ID for new users o default minimum group ID for new groups o status reporting, including o next available user ID greater than or equal to the minimum user ID o GUI interface definition etc. Obviously, if the GUI interface definition and the user input used a commit/cancel interaction, then it would be possible to implement dialog interactions for user input in the UI case: UADMIN> BEGIN ADD USER UADMIN] NAME 'Terry Lambert' UADMIN] USER terry UADMIN] ID 501 UADMIN] GROUP 20 UADMIN] +GROUP 0 UADMIN] +GROUP 552 UADMIN] HOME /home/terry UADMIN] SHELL /bin/csh UADMIN] EXPIRE PASSWORD NEVER UADMIN] EXPIRE ACCOUNT NEVER UADMIN] END ACCEPT UADMIN> Each time the 'name' field changed, for instance: UADMIN> BEGIN ADD USER UADMIN] NAME 'Terry' UADMIN] USER terry UADMIN] ID 501 UADMIN] GROUP 20 UADMIN] +GROUP 0 UADMIN] +GROUP 552 UADMIN] NAME 'Lambert' UADMIN] HOME /home/terry UADMIN] SHELL /bin/csh UADMIN] NAME 'Terry Lambert' UADMIN] EXPIRE PASSWORD NEVER UADMIN] EXPIRE ACCOUNT NEVER UADMIN] END ACCEPT UADMIN> And then the current record is committed/uncommitted as an agregate: UADMIN] END ACCEPT UADMIN] END CANCEL The point being that you could compartmentalize a dialog that way rather easily, such that the GUI program would not need to have specific knowledge of what command line utility it was to be used for. Pretty obviously, the same GUI or text UI (both should be provided) could be used to do disk partitioning, or whatever, as long as the utility being front-ended provided the interagtion grammar via a standard interaction mechanism to the (G)UI. Obviously, there needs to be the concept of "environment" and "get default from environment", with the environent maintained by the utility being fronted, and not the front end itself. For instance, ID probably wants to be defaulted to 'NEXT_UNUSED_ID' from the utility environment, etc.. It's a pretty obvious dialog interaction model, if you think about it, and it prevents spreading the maintenance hassle over a potentially large number of programs, which may or may not be correctly synced. For a good start on the uadmin utility: makefile: ========================================================================== LEX= lex -l YACC= yacc CFLAGS= -DYYDEBUG=1 YSRC= uadmin.y LSRC= uadmin.l P1= uadmin P1SRCS= y.tab.c lex.yy.c defaults.c users.c groups.c P1OBJS= $(P1SRCS:.c=.o) all: $(P1) $(P1): $(P1OBJS) $(CC) -o $@ $> -lln -ly y.tab.c y.tab.h: $(YSRC) $(YACC) -d $> lex.yy.c: $(LSRC) $(LEX) $> clean: @rm -f $(P1) $(P1OBJS) clobber: clean @rm -f y.tab.c lex.yy.c ========================================================================== uadmin.l: ========================================================================== %{ #include "y.tab.h" #include <math.h> #include <string.h> extern double vbltable[ 26]; %} %% show { return SHOW; } nextuid { return NEXTUID; } group { return GROUP; } user { return USER; } default { return DEFAULT; } verbose { return VERBOSE; } [A-Za-z][A-Za-z0-9]* { yylval.sval = strdup(yytext); return NAME; } [0-9]* { yylval.ival = atoi(yytext); return NUM; } ([0-9]+|([0-9]*\.[0.9]+)([eE][-+]?[0-9]+)?) { yylval.dval = atof(yytext); return NUMBER; } [ \t] ; /* ignore whitespace*/ [a-z] { yylval.vblno = yytext[0] - 'a'; return XNAME; } "$" { return 0; /* end of input*/ } \n | . { return yytext[ 0]; } %% ========================================================================== uadmin.y ========================================================================== %{ /* * This file is machine generated. */ %} %union { char *sval; int ival; } %token <dval> NUMBER %token <ival> NUM %type <ival> vopt %token SHOW %token NEXTUID %token GROUP %token USER %token DEFAULT %token VERBOSE %token <sval> NAME %left '-' '+' %left '*' '/' %nonassoc UMINUS %type <dval> expression %% session: prompt_statement | session prompt_statement ; prompt_statement: statement '\n' { prompt(); } ; statement: /* nothing*/ | show_expr /* | expression { printf("= %g\n", $1); }*/ ; show_expr: SHOW NEXTUID { nextuid_show(); } | SHOW USER NAME vopt { user_show_name( $3, $4); } | SHOW USER NUM vopt { user_show_num( $3, $4); } | SHOW USER '*' vopt { user_show_all( $4); } | SHOW GROUP NAME vopt { group_show_name( $3, $4); } | SHOW GROUP NUM vopt { group_show_num( $3, $4); } | SHOW GROUP '*' vopt { group_show_all( $4); } | SHOW DEFAULT NAME { default_show_name( $3); } | SHOW DEFAULT '*' { default_show_all(); } ; vopt: /* empty*/ { $$ = 0; } | VERBOSE { $$ = 1; } expression: expression '+' expression { $$ = $1 + $3; } | expression '-' expression { $$ = $1 - $3; } | expression '*' expression { $$ = $1 * $3; } | expression '/' expression { if( $3 == 0.0) yyerror("divide by zero"); else $$ = $1 / $3; } | '-' expression %prec UMINUS { $$ = -$2; } | '(' expression ')' { $$ = $2; } | NUMBER ; %% void yyerror(s) const char *s; { printf("%s\n", s); } void prompt(void) { printf( "UADMIN> "); } int main( int ac, char **av) { prompt(); while( yyparse()) { /* syntax error*/ continue; } printf( "\n"); return 0; } ========================================================================== defaults.c ========================================================================== /* * defaults.c * * Routines to display and modify the program defaults */ void default_show_name( char *str) { printf( "default by name '%s'\n", str); } void default_show_all( void) { printf( "all defaults\n"); } void nextuid_show( void) { printf( "the next uid is ???\n"); } /* * EOF -- This file has not been truncated */ ========================================================================== groups.c ========================================================================== /* * groups.c * * Routines to display and modify the group database */ void group_show_name( char *str, int verbose) { printf( "group by name '%s' %s\n", str, verbose ? "verbose" : "terse"); free( str); /* was strdup'ed by lex*/ } void group_show_num( int num, int verbose) { printf( "group by num '%d' %s\n", num, verbose ? "verbose" : "terse"); } void group_show_all( int verbose) { printf( "all groups %s\n", verbose ? "verbose" : "terse"); } /* * EOF -- This file has not been truncated */ ========================================================================== users.c ========================================================================== /* * users.c * * Routines to display and modify the user database */ #include <sys/types.h> #include <sys/param.h> /* NGROUPS*/ #include <stdio.h> #include <pwd.h> #include <grp.h> char * expire_time( time_t *timep) { return( "NEVER"); } char * groupname( int gid) { struct group *groupp; if( ( groupp = getgrgid( gid)) == NULL) return( "<NO NAME: getgrgid error"); else return( groupp->gr_name); } void user_show( struct passwd *userp, int verbose) { int groups[ NGROUPS ]; int ngroups = NGROUPS; int i; printf( "NAME '%s'\n", userp->pw_gecos); printf( "USER %s\n", userp->pw_name); printf( "ID %d\n", userp->pw_uid); if( verbose && userp->pw_passwd[ 0] != '*') { printf( "PASSWORD '%s'\n", userp->pw_passwd); } printf( "GROUP %d (%s)\n", userp->pw_gid, groupname( userp->pw_gid)); if( verbose && getgrouplist( userp->pw_name, userp->pw_gid, groups, &ngroups) != -1) { for( i = 0; i < ngroups; i++) { if( groups[ i] == userp->pw_gid) continue; /* skip default*/ printf( "+GROUP %d (%s)\n", groups[ i], groupname( groups[ i])); } } printf( "HOME %s\n", userp->pw_dir); printf( "SHELL %s\n", userp->pw_shell); printf( "EXPIRE PASSWORD %s\n", expire_time(&userp->pw_change)); printf( "EXPIRE ACCOUNT %s\n", expire_time(&userp->pw_expire)); } void user_show_name( char *str, int verbose) { struct passwd *userp; printf( "user by name '%s' %s\n", str, verbose ? "verbose" : "terse"); if( ( userp = getpwnam( str)) == NULL) { printf( "No such user '%s'\n", str); goto error; } user_show( userp, verbose); error: free( str); /* was strdup'ed by lex*/ } void user_show_num( int num, int verbose) { struct passwd *userp; printf( "user by num '%d' %s\n", num, verbose ? "verbose" : "terse"); if( ( userp = getpwuid( num)) == NULL) { printf( "No such user '%d'\n", num); goto error; } user_show( userp, verbose); error: return; } void user_show_all( int verbose) { printf( "all users %s\n", verbose ? "verbose" : "terse"); } /* * EOF -- This file has not been truncated */ ========================================================================== Terry Lambert terry@lambert.org --- Any opinions in this posting are my own and not those of my present or previous employers.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199605272104.OAA08998>