]> git.cameronkatri.com Git - pw-darwin.git/commitdiff
Wolfram Schneider <wosch@cs.tu-berlin.de>:
authorJordan K. Hubbard <jkh@FreeBSD.org>
Mon, 9 Jan 1995 11:26:31 +0000 (11:26 +0000)
committerJordan K. Hubbard <jkh@FreeBSD.org>
Mon, 9 Jan 1995 11:26:31 +0000 (11:26 +0000)
o manpage
o save configuration in /etc/adduser.conf
o send message file (/etc/adduser.message)
Submitted by: woschcs.tu-berlin.de

adduser/adduser.8
adduser/adduser.perl

index 6ed7fb6119869fd83f7c95e503a0c564a321809e..eb2ae8f726ca2d727f820d0b560541392fbd084f 100644 (file)
-.Dd January 3, 1994
-.Dt ADDUSER 8
-.Os FreeBSD 
+.\"
+.\" (c) Copyright 1995 Wolfram Schneider. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"   This product includes software developed by Wolfram Schneider
+.\" 4. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" /usr/sbin/adduser - add new user(s)
+.\"
+.\" Bugs: sure (my english!)
+.\"   Email: Wolfram Schneider <wosch@cs.tu-berlin.de>
+.\"
+.\" $Id: adduser.8,v 1.4 1995/01/08 17:40:20 w Exp w $
+.\"
 
+.Dd Jan, 9, 1995
+.Dt ADDUSER 8
+.Os FreeBSD 2.1
 .Sh NAME
 .Nm adduser
-
-.Sh SYNOPSIS
-.Nm
+.Nd command for adding new users
 
 .Sh DESCRIPTION
+.Nm Adduser 
+is a simple program for adding new users. Adduser check
+passwd, group and shell database. It create passwd/group entry,
+HOME-directory, dotfiles and send new user a welcome message.
+
+.Sh RESTRICTION
+.Bl -tag -width Ds -compact
+.It Sy username
+Login name. Only lowercase characters or digits.
+.It Sy fullname
+Firstname an surname. 
+.Ql Pa \:
+not allowed.
+.It Sy shell
+Only valid shells from shell database. 
+.It Sy uid
+Automatic generated.
+.It Sy gid
+Automatic generated. Gid and uid are equal (if possible). Groupname
+is the same as username.
+.El
+
+.Sh CONFIGURATION
+.Bl -tag -width Ds -compact
+.It Sy 1.
+Read intern variables.
+.It Sy 2.
+Read config file (/etc/adduser.conf)
+.It Sy 3.
+Parse command line options
+.El
+
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl help
+Print options and exit.
+.It Fl silent
+Few warnings, question, bugreports. 
+.It Fl verbose
+Many warning, question. Recommended for noivice users.
+.It Fl debug
+Debuging.
+.It Fl noconfig
+Do not read config file.
+.It Fl home Ar partition
+Default home partition where all users located.
+.It Fl shell Ar shell 
+Default shell for new users.
+.It Fl dotdir Ar directory
+Copy files from .Ar directory into the HOME-directory of new users.
+.Ql Pa dot.foo
+files renamed to 
+.Ql Pa .foo
+Don't copy files if
+.Ar directory 
+equal with 
+.Ar no
+.It Fl message Ar file
+Send new users a welcome message.
+Don't send message if
+.Ar file 
+equal with
+.Ar no
+.It Fl create_conf
+Create new config and message file and exit. 
+
+.Sh FORMATS
+.Bl -tag -width Ds -compact
+.Ql Pa #
+is a commemt.  
+
+.P
+.It Sy config file
 .Nm Adduser
-is a perl script for adding users to the system.  Still awaiting
-a man page entry for it but for now you can probably get away with just
-typing
-.Nm adduser
-and filling in the blanks.
+read and write this file. 
+See /etc/adduser.conf for more details.
+.It Sy message file
+Eval variables in this file. See /etc/adduser.message for more
+details.
+.El
+
 .Sh FILES
-Beats me.  Wolfram?  Man page please! :-)
+.Bl -tag -width /etc/master.passwdxx -compact
+.It Pa /etc/master.passwd
+user database
+.It Pa /etc/group
+group database
+.It Pa /etc/shells
+shell database
+.It Pa /etc/adduser.conf
+config file for adduser
+.It Pa /etc/adduser.message
+message file for adduser
+.It Pa /usr/share/skel
+skeletal login directory
+.It Pa /var/log/adduser
+logfile for adduser
+.El
+
 .Sh SEE ALSO
-.Xr pwd_mkdb 8
-.Xr perl 1
+.Xr chpass 1 ,
+.Xr finger 1 ,
+.Xr passwd 1 ,
+.Xr aliases 5 ,
+.Xr passwd 5 ,
+.Xr group 5 ,
+.Xr shells 5 ,
+.Xr pwd_mkdb 8 ,
+.Xr vipw 8
+
+.\" .Sh BUGS
 
 .Sh HISTORY
-The adduser command first appeared in FreeBSD 2.0C (12-94) by Gary Clark, II.
-This newer version was submitted by Wolfram Schneider.
+The
+.Nm
+command appeared in FreeBSD 2.1
+
+.\" .Sh AUTHOR
+.\" Wolfram Schneider, Berlin
index 60e89e73c8924faf2c15f999b786ac2861c44058..ca07f3095626ca4cca3188058cbb6dc7c551a4e6 100644 (file)
@@ -12,7 +12,7 @@
 #    documentation and/or other materials provided with the distribution.
 # 3. All advertising materials mentioning features or use of this software
 #    must display the following acknowledgement:
-#   This product includes software developed by Wolfram Schneider
+#    This product includes software developed by Wolfram Schneider
 # 4. The name of the author may not be used to endorse or promote products
 #    derived from this software without specific prior written permission
 #
 # Bugs: sure (my english!)
 #   Email: Wolfram Schneider <wosch@cs.tu-berlin.de>
 #
-# $Id: adduser,v 1.17 1995/01/02 00:08:43 w Exp w $
+# $Id: adduser,v 1.40 1995/01/08 17:40:09 w Exp w $
 #
 
+# read variables
 sub variables {
-    $verbose = 1;
-    $batch = 0;                         # batch mode
-    $defaultpasswd = 0;
-    $dotdir = "/usr/share/skel";
-
-    if (1) {
-    $home = "/home";
-    $shells = "/etc/shells";
-    $passwd = "/etc/master.passwd";
+    $verbose = 1;                       # verbose = [0-2]
+    $defaultpasswd = "no";              # use password for new users
+    $dotdir = "/usr/share/skel";        # copy dotfiles from this dir
+    $send_message = "/etc/adduser.message";  # send message to new user
+    $config = "/etc/adduser.conf";      # config file for adduser
+    $config_read = 1;                   # read config file
+    $logfile = "/var/log/adduser";      # logfile
+    $home = "/home";                    # default HOME
+    $etc_shells = "/etc/shells";
+    $etc_passwd = "/etc/master.passwd";
     $group = "/etc/group";
-    $pwd_mkdb = "pwd_mkdb -p";
-    } else {
+    $pwd_mkdb = "pwd_mkdb -p";          # program for building passwd database
+
+    if ($test) {
     $home = "/home/w/tmp/adduser/home";
-    $shells = "./shells";
-    $passwd = "./master.passwd";
+    $etc_shells = "./shells";
+    $etc_passwd = "./master.passwd";
     $group = "./group";
     $pwd_mkdb = "pwd_mkdb -p -d .";
+    $config = "adduser.conf";
+    $send_message = "./adduser.message";
+    $logfile = "./log.adduser";
     }
 
+    $ENV{'PATH'} = "/sbin:/bin:/usr/sbin:/usr/bin";
+    # List of directories where shells located
     @path = ('/bin', '/usr/bin', '/usr/local/bin');
+    # common shells, first element has higher priority
     @shellpref = ('bash', 'tcsh', 'ksh', 'csh', 'sh');
+
+    $defaultshell = 'bash'; # defaultshell if not empty
+
     $uid_start = 1000;      # new users get this uid
-    $uid_end   = 32000;
+    $uid_end   = 32000;     # max. uid
 
     # global variables
     $username = '';         # $username{username} = uid
@@ -66,15 +78,16 @@ sub variables {
     $pwgid = '';            # $pwgid{pwgid} = username; gid from passwd db
     $groupname ='';         # $groupname{groupname} = gid
     $gid = '';              # $gid{gid} = groupname;    gid form group db
-    $defaultshell = '';
     @passwd_backup = '';
+    @message_buffer = '';
+    @user_variable_list = '';   # user variables in /etc/adduser.conf
+    $do_not_delete = '## DO NOT DELETE THIS LINE!';
 }
 
-# read shell database
-# See also: shells(5)
+# read shell databasem, see also: shells(5)
 sub shells_read {
     local($s, @dummy);
-    open(S, $shells) || die "$shells:$!\n";
+    open(S, $etc_shells) || die "$shells:$!\n";
     while(<S>) {
         if (/^[ \t]*\//) {
             ($s, @dummy) = split;
@@ -90,83 +103,128 @@ sub shells_read {
 # add new/local shells
 sub shells_add {
     local($e,$dir,@list);
+
     foreach $e (@shellpref) {
         if (!$shell{$e}) {
             foreach $dir (@path) {
                 if (-x "$dir/$e") {
-                    push(@list, "$dir/$e") if
-                    &confirm_yn("Found shell: $dir/$e. Add to $shells?", "yes");
+                    if ($verbose) {
+                        if (&confirm_yn("Found shell: $dir/$e. Add to $etc_shells?", "yes")) {
+                            push(@list, "$dir/$e");
+                            push(@shellpref, "$dir/$e");
+                            $shell{&basename("$dir/$e")} = "$dir/$e";
+                            $changes++;
+                        }
+                    } else {
+                        print "Found unused shell: $dir/$e\n";
+                    }
                 }
             }
         }
     }
-    if ($#list >= 0) {
-        foreach $e (@list) {
-            $shell{&basename($e)} = $e;
-            #print "$e\n";
-        }
-        &append_file($shells, @list);
-    }
+    &append_file($etc_shells, @list) if $#list >= 0;
 }
 
-# choise your favourite shell
-sub shells_pref {
+# choise your favourite shell an return the shell
+sub shell_default {
     local($e,$i,$s);
+    local($sh) = $defaultshell;
+
+    # $defaultshell is empty, looking for another shell
+    if (!$sh || !$shell{$sh}) {
+        $i = 0;
+        while($i < $#shellpref) {
+            last if $shell{$shellpref[$i]};
+            $i++;
+        }
+        $sh = $shellpref[$i];
 
-    $i = 0;
-    while($i < $#shellpref) {
-        last if $shell{$shellpref[$i]};
-        $i++;
+        if (!$sh || !$shell{$sh}) {
+            warn "No valid shell found in $etc_shells.\n";
+            $sh = '';
+        }
+    }
+    if ($verbose) {
+        $s = &confirm_list("Enter your default shell:", 0,
+                            $sh, sort(keys %shell));
+        print "Your default shell is: $s -> $shell{$s}\n"; 
+        $changes++ if $s ne $sh;
+        return $s;
+    } else {
+        return $sh;
     }
-    $s = &confirm_list("Enter Your default shell:", 0,
-                        $shellpref[$i], sort(keys %shell));
-    print "Your default shell is: $s -> $shell{$s}\n" if $verbose;
-    $defaultshell = $s;
 }
 
-# return default home partition
+# return default home partition, create base directory if nesseccary
 sub home_partition {
     local($h);
+    $oldverbose = $verbose if !defined $oldverbose;
+
+    if ($verbose) {
+        $h = &confirm_list("Enter your default HOME partition:", 1, $home, "");
+    } else {
+        $h = $home;
+    }
+
+    if ($h !~ "^/") {
+        warn "Please use absolute path for home: ``$h''.\n";
+        $verbose++;
+        $h =~ s|^[^/]+||;
+        $home = $h;
+        return &home_partition;
+    }
 
-    $h = &confirm_list("Enter Your default HOME partition:", 1, $home, "");
-    if (-e "$h") {
+    if (-e $h) {
         if (!(-d _ || -l $h)) {
             warn "$h exist, but is it not a directory or symlink!\n";
+            $verbose++;
             return &home_partition;
         }
         if (! -w _) {
             warn "$h is not writable!\n";
+            $verbose++;
             return &home_partition;
         }
     } else {
+        $verbose++;
         return &home_partition unless &mkdirhier($h);
     }
-       
-       $home = $h;
+
+    $verbose = $oldverbose;
+    undef $oldverbose;
+    $changes++ if $h ne $home;
     return $h;
 }
 
 # check for valid passwddb
 sub passwd_check {
-    print "Check $passwd\n" if $verbose > 0;
-    system("$pwd_mkdb $passwd");
-    die "\nInvalid $passwd - cannot add any users!\n" if $?;
+    print "Check $etc_passwd\n" if $verbose > 0;
+    system("$pwd_mkdb $etc_passwd");
+    die "\nInvalid $etc_passwd - cannot add any users!\n" if $?;
 }
 
 # read /etc/passwd
 sub passwd_read {
-    local($un, $pw, $ui, $gi);
+    local($un, $pw, $ui, $gi, $sh, %shlist);
 
-    open(P, "$passwd") || die "$passwd: $!\n";
+    print "Check $group\n" if $verbose;
+    foreach $sh (keys %shell) {
+        $shlist{$shell{$sh}} = $sh; #print "$sh $shell{$sh}\n";
+    }
+
+    open(P, "$etc_passwd") || die "$passwd: $!\n";
     while(<P>) {
         chop;
         push(@passwd_backup, $_);
-        ($un, $pw, $ui, $gi) = (split(/:/, $_))[0..3];
+        ($un, $pw, $ui, $gi, $sh) = (split(/:/, $_))[0..3,9];
         print "$un already exist with uid: $username{$un}!\n"
             if $username{$un};
         $username{$un} = $ui;
         print "User $un: uid $ui exist twice: $uid{$ui}\n"
-            if $uid{$ui} && $verbose;
+            if $uid{$ui} && $verbose && $ui;    # don't warn for uid 0
+        print "User $un: illegal shell: ``$sh''\n" 
+            if ((!$shlist{$sh} && $sh) &&
+                ($un !~ /^(bin|uucp|falcon|nobody)$/));
         $uid{$ui} = $un;
         $pwgid{$gi} = $un;
     }
@@ -197,31 +255,35 @@ sub group_check {
     foreach $e (keys %pwgid) {
         if (!$gid{$e}) {
             $user = $pwgid{$e};
-            warn "Gid $e is defined in $passwd for user ``$user''\n";
-            warn "but not in $group!\n";
+            warn "User ``$user'' has gid $e but a group with this " .
+                 "gid does not exist.\n";
+            #warn "Gid $e is defined in $etc_passwd for user ``$user''\n";
+            #warn "but not in $group!\n";
             if ($groupname{$user}) {
                 warn <<EOF;
-I'm confused! Maybe the gids ($e <-> $groupname{$user}) for user ``$user''
-in $passwd & $group are wrong.
-See $passwd ``$user:*:$username{$user}:$e''
-See $group ``$user:*:$groupname{$user}''
+Maybe the gids ($e <-> $groupname{$user}) for user+group ``$user'' are wrong.
 EOF
             } else {
                 push(@list, "$user:*:$e:$user")
-                if (&confirm_yn("Add group``$user'' gid $e to $group?", "y"));
+                if $verbose && 
+                    &confirm_yn("Add group``$user'' gid $e to $group?", "y");
             }
         }
     }
     &append_file($group, @list) if $#list >= 0;
 }
 
+#
+# main loop for creating new users
+#
 sub new_users {
     local(@userlist) = @_;
     local($name);
     local($defaultname) = "a-z0-9";
 
-    print "\nOk, let's go.\n";
-    print "Don't worry about mistakes. I ask You later for " .
+    print "\n" if $verbose;
+    print "Ok, let's go.\n" .
+          "Don't worry about mistakes. I ask you later for " .
           "correct input.\n" if $verbose;
 
     while(1) {
@@ -246,10 +308,10 @@ sub new_users {
     print <<EOF;
 
 Name:     $name
-Passwd:   none, is empty
+Passwd:   no, is empty
 Fullname: $fullname
 Uid:      $u_id
-Gid:      $g_id
+Gid:      $g_id ($name)
 HOME:     $home/$name
 Shell:    $sh
 EOF
@@ -257,11 +319,11 @@ EOF
         local($new_entry) =
             "$name::$u_id:$g_id::0:0:$fullname:$home/$name:$sh";
 
-        &append_file($passwd, $new_entry);
+        &append_file($etc_passwd, $new_entry);
 
-        system("$pwd_mkdb $passwd");
+        system("$pwd_mkdb $etc_passwd");
         if ($?) {
-            local($crash) = "$passwd.crash$$";
+            local($crash) = "$etc_passwd.crash$$";
             warn "$pwd_mkdb failed, try to restore ...\n";
 
             open(R, "> $crash") || die "Sorry, give up\n";
@@ -272,7 +334,7 @@ EOF
 
             system("$pwd_mkdb $crash");
             die "Sorry, give up\n" if $?;
-            die "Successfully restore $passwd. Exit.\n";
+            die "Successfully restore $etc_passwd. Exit.\n";
         }
         # Add new group
         &append_file($group, "$name:*:$g_id:$name")
@@ -287,32 +349,64 @@ EOF
         $gid{$g_id} = $name;
 
         print "Added user ``$name''\n";
+
+        if ($send_message && $send_message ne "no") {
+            local($m) = &confirm_list("Send message to ``$name'' and:",
+                1, "no", ("second_mail_address", "no"));
+            if ($verbose) {
+                print "send message to: ``$name''";
+                if ($m eq "no") { print "\n" } 
+                    else { print " and ``$m''\n"}
+            }
+            $m = "" if $m eq "no";
+            local($e);
+            if (!open(M, "| mail -s Welcome $name $m")) {
+                warn "Cannot send mail to: $name $m!\n";
+            } else {
+                foreach $e (@message_buffer) {
+                    print M eval "\"$e\"";
+                }
+                close M;
+            }
+        }
+
         local($a) = &confirm_yn("Change password", $defaultpasswd);
-        if (($a && $defaultpasswd) || (!$a && !$defaultpasswd)) {
+        local($empty_password) = "";
+        if (($a && $defaultpasswd ne "no" && $defaultpasswd) || 
+            (!$a && $defaultpasswd eq "no")) {
             while(1) {
                 system("passwd $name");
-                last unless $?;
+                if (!$?) { $empty_password = "*"; last }
                 last unless
                     &confirm_yn("Passwd $name failed. Try again?", "yes");
             }
         }
-       &home_create($name);
-       }
-       if (&confirm_yn("Continue with next user?", "yes")) {
-               &new_users;
+        &adduser_log("$name:$empty_password:$u_id:$g_id($name):$fullname");
+        &home_create($name);
+    }
+    if (&confirm_yn("Continue with next user?", "yes")) {
+        print "\n" if !$verbose;
+        &new_users;
     } else {
         print "Good by.\n" if $verbose;
     }
 }
 
-#
-sub password_pref {
-    $defaultpasswd = !&confirm_yn("Use empty passwords", "yes");
+# ask for password usage
+sub password_default {
+    local($p) = $defaultpasswd;
+    if ($verbose) {
+        $p = &confirm_yn("Use passwords", $defaultpasswd);
+        $changes++ unless $p;
+    }
+    return "yes" if (($defaultpasswd eq "yes" && $p) ||
+                     ($defaultpasswd eq "no" && !$p));
+    return "no";    # otherwise
 }
 
 # misc
 sub check_root {
-    die "You are not root!\n" if $<;
+    die "You are not root!\n" if $< && !$test;
 }
 
 sub usage {
@@ -324,13 +418,35 @@ OPTIONS:
 -help                   this help
 -silent                 opposite of verbose
 -verbose                verbose
+-debug                  verbose verbose
+-noconfig               don't read config-file
 -home home              default HOME partition [$home]
--shell shell            default SHELL
--dotdir dir             copy files from dir, default $dotdir
+-shell shell            default SHELL [$defaultshell]
+-dotdir dir             copy files from dir, default [$dotdir]
+-message file           send message to new users [$send_message]
+-create_conf            create configuration/message file and exit
 USAGE
     exit 1;
 }
 
+# print banner
+sub copyright {
+    print <<EOF;
+(c) Copyright 1995 Wolfram Schneider <wosch@cs.tu-berlin.de>
+EOF
+}
+
+# hints
+sub hints {
+    if ($verbose) {
+        print "Use option ``-silent'' if you don't want see " .
+              "some warnings & questions.\n\n";
+    } else {
+        print "Use option ``-verbose'' if you want see more warnings & " .
+              "questions \nor try to repair bugs.\n\n";
+    }
+}
+
 #
 sub parse_arguments {
     local(@argv) = @_;
@@ -338,14 +454,19 @@ sub parse_arguments {
     while ($_ = $argv[0], /^-/) {
         shift @argv;
         last if /^--$/;
-        if    (/^--?(debug|verbose)$/)  { $verbose = 1 }
-        elsif (/^--?(silent|guru|wizard)$/) { $verbose = 0 }
-        elsif (/^--?(verbose)$/)        { $verbose = 1 }
+        if    (/^--?(verbose)$/)        { $verbose = 1 }
+        elsif (/^--?(silent|guru|wizard|quit)$/) { $verbose = 0 }
+        elsif (/^--?(debug)$/)          { $verbose = 2 }
         elsif (/^--?(h|help|\?)$/)      { &usage }
         elsif (/^--?(home)$/)           { $home = $argv[0]; shift @argv }
-        elsif (/^--?(shell)$/)          { $shell = $argv[0]; shift @argv }
+        elsif (/^--?(shell)$/)          { $defaultshell = $argv[0]; 
+                                          shift @argv }
         elsif (/^--?(dotdir)$/)         { $dotdir = $argv[0]; shift @argv }
-        elsif (/^--?(batch)$/)          { $batch = 1; }
+        elsif (/^--?(message)$/)        { $send_message = $argv[0]; shift @argv;
+                                          $sendmessage = 1; }
+        # see &config_read
+        elsif (/^--?(create_conf)$/)    { &create_conf; }
+        elsif (/^--?(noconfig)$/)       { $config_read = 0; }
         else                            { &usage }
     }
     #&usage if $#argv < 0;
@@ -357,19 +478,68 @@ sub basename {
     return $name;
 }
 
-#
+sub dirname {
+    local($name) = @_;
+    $name =~ s|[/]+$||;             # delete any / at end 
+    $name =~ s|[^/]+$||;
+    $name = "/" unless $name;       # dirname of / is / 
+    return $name;
+}
+
+# return 1 if $file is a readable file or link
+sub filetest {
+    local($file, $verb) = @_;
+
+    if (-e $file) {
+        if (-f $file || -l $file) {
+            return 1 if -r _;
+            warn "$file unreadable\n" if $verb;
+        } else {
+            warn "$file is not a plain file or link\n" if $verb;
+        }
+    }
+    return 0; 
+}
+
+# create configuration files and exit
+sub create_conf {
+    $create_conf = 1;
+    &message_create($send_message);
+    &config_write(1);
+    exit(0);
+}
+
+# log for new user in /var/log/adduser
+sub adduser_log {
+    local($string) = @_;
+    local($e);
+
+    return 1 if $logfile eq "no";
+
+    local($sec, $min, $hour, $mday, $mon, $year) = localtime;
+    $mon++;
+
+    foreach $e ('sec', 'min', 'hour', 'mday', 'mon', 'year') {
+        # '7' -> '07'
+        eval "\$$e = 0 . \$$e" if (eval "\$$e" < 10);   
+    }
+
+    &append_file($logfile, "$year/$mon/$mday $hour:$min:$sec $string");
+}
+
+# create HOME directory, copy dotfiles from $dotdir to $HOME
 sub home_create {
     local($name) = @_;
     local(@list);
     local($e,$from, $to);
 
-    print "Create HOME directory\n";
+    print "Create HOME directory\n" if $verbose;
     if(!mkdir("$home/$name", 0755)) {
-        warn "Cannot create HOME directory for $name: $!\n";
+        warn "Cannot create HOME directory for user ``$home/$name'': $!\n";
         return 0;
     }
     push(@list, "$home/$name");
-    if ($dotdir) {
+    if ($dotdir && $dotdir ne "no") {
         opendir(D, "$dotdir") || warn "$dotdir: $!\n";
         foreach $from (readdir(D)) {
             if ($from !~ /^(\.|\.\.)$/) {
@@ -382,8 +552,8 @@ sub home_create {
         }
         closedir D;
     }
-       #warn "Chown: $name, $name, @list\n";
-       #chown in perl does not work 
+    #warn "Chown: $name, $name, @list\n";
+    #chown in perl does not work 
     system("chown $name:$name @list") || warn "$!\n" && return 0;
     return 1;
 }
@@ -423,10 +593,14 @@ sub mkdirhier {
 # If !$allow accept only elements from @list.
 sub confirm_list {
     local($message, $allow, $confirm, @list) = @_;
-    local($read, $c);
+    local($read, $c, $print);
+
+    $print = "$message " if $message;
+    $print .= "@list ";
+    print "$print";
+    print "\n " if length($print) > 50;
+    print "[$confirm]: ";
 
-    print "$message " if $message;
-    print "@list [$confirm]: ";
     chop($read = <STDIN>);
     $read =~ s/^[ \t]*//;
     $read =~ s/[ \t\n]*$//;
@@ -441,7 +615,9 @@ sub confirm_list {
 }
 
 # YES or NO question
-# $confirm => 'y' or 'n'. Return true if answer 'y' (or 'n')
+# return 1 if &confirm("message", "yes") and answer is yes
+#       or if &confirm("message", "no") an answer is no
+# otherwise 0
 sub confirm_yn {
     local($message, $confirm) = @_;
     local($yes) = "^(yes|YES|y|Y)$";
@@ -472,12 +648,116 @@ sub confirm_yn {
 }
 
 # test if $dotdir exist
-sub dotdir_check {
-    return 1 if -e $dotdir && -r _ && (-d _ || -l $dotdir);
-    warn "Directory: $dotdir does not exist or unreadable. " .
-         "Cannot copy dotfiles!\n";
-    $dotdir = '';
-    return 0;
+sub dotdir_default {
+    #$dotdir = "no" unless $dotdir;
+    local($dir) = $dotdir;
+    local($oldverbose) = $verbose;
+
+    if ($dir !~ "^/") {
+        warn "Please use absulote path for dotdir: ``$dir''\n";
+        $dir = "no";
+        $verbose++ unless $verbose;
+    }
+    while($verbose) {
+        $dir = &confirm_list("Copy dotfiles from:", 1, 
+            $dir, ("no", $dir));
+        last if $dir eq "no";
+        last if (-e $dir && -r _ && (-d _ || -l $dir));
+        last if !&confirm_yn("Dotdir ``$dir'' is not a dir. Try again", "yes");
+    }
+    unless (-e $dir && -r _ && (-d _ || -l $dir)) {
+        warn "Directory: $dir does not exist or unreadable.\n" 
+            if $dir ne "no";
+        warn "Do not copy dotfiles.\n"; 
+        $dir = "no";
+    }
+    $changes++ if $dir ne $dotdir;
+    $verbose = $oldverbose;
+    return $dir;
+}
+
+# ask for messages to new users
+sub message_default {
+    local($file) = $send_message;
+    local(@d); 
+
+    push(@d, $file);
+    push(@d, "no") if $file ne "no";
+    if (!&filetest($file, 1) && $file ne "no") {
+        if (! -e $file && 
+                &confirm_yn("Create message file ``$file''?", "yes")) {
+            &message_create($file);
+        } else {
+            $file = "no";
+        }
+    }
+
+    while($verbose) {
+        $file = &confirm_list("Send message from file: ", 1, 
+            $file, @d);
+        last if $file eq "no";
+        if (&filetest($file, 1)) {
+            last;
+        } else {
+            &message_create($file) if ! -e $file &&
+                &confirm_yn("Create ``$file''?", "yes");
+            last if &filetest($file, 0);
+            last if !&confirm_yn(
+                "File ``$file'' does not exist, try again?", "yes");
+        }
+    }
+    if ($file eq "no" || !&filetest($file, 0)) {
+        warn "Do not send message\n" if $verbose;
+        $file = "no";
+    } else {
+        &message_read($file);
+    }
+
+    $changes++ if $file ne $send_message;
+    return $file;
+}
+
+# create message file
+sub message_create {
+    local($file) = @_;
+    local($dir) = &dirname($file);
+
+    if (! -d $dir) {
+        return 0 unless &mkdirhier($dir);
+    }
+    if (!open(M, "> $file")) {
+        warn "Messagefile ``$file'': $!\n"; return 0;
+    }
+    print M <<EOF;
+#
+# Message file for adduser(8)
+#   comment: ``#''
+#   defaultvariables: \$name, \$fullname
+#   other variables:  see /etc/adduser.conf after 
+#                     line  ``$do_not_delete''
+#
+
+\$fullname,
+
+your account ``\$name'' was created. Have fun!
+
+See also chpass(1), finger(1), passwd(1)
+EOF
+    close M;
+    return 1;
+}
+
+# read message file into buffer
+sub message_read {
+    local($file) = @_;
+    @message_buffer = '';
+
+    if (!open(R, "$file")) {
+        warn "File ``$file'':$!\n"; return 0;
+    }
+    while(<R>) {
+        push(@message_buffer, $_) unless /^[ \t]*#/;
+    }
 }
 
 # write @list to $file with file-locking
@@ -523,6 +803,7 @@ sub next_id {
     return ($uid_start, $gid_start);
 }
 
+# cp(1)
 sub cp {
     local($from, $to, $tilde) = @_;
 
@@ -552,25 +833,164 @@ sub cp {
     return 1;
 }
 
+# read config file
+sub config_read {
+    local($opt) = @_;
+    local($ev);     # evaluate variable or list
+    local($user_flag) = 0;
+       
+    return 1 if $opt =~ /-(noconfig|create_conf)/;    # don't read config file
+
+    if ($config_read && -f $config) {
+        if(!open(C, "$config")) {
+            warn "$config: $!\n"; return 0;
+        }
+        print "Read config file: $config" if $verbose > 1;
+        while(<C>) {
+            chop;
+            /^$do_not_delete/ && $user_flag++;
+            if (/^[ \t]*[a-zA-Z_-]+[ \t]*=/) {
+                # prepare for evaluating
+                s/#.*$//;
+                s/^[ \t]+//;
+                s/[ \t;]+$//;
+                s/[ \t]*=[ \t]*/=/;
+                s/=(.*)/="$1"/ unless /=['"(]/;
+
+                print "$_\n" if $verbose > 1;
+                if (/=\(/) {    # perl list
+                    $ev = "\@";
+                } else {            # perl variable
+                    $ev = "\$";
+                }
+                eval "$ev$_" || warn "Ignore garbage: $ev $_\n";
+                push(@user_variable_list, "$_\n") if $user_flag;
+            }
+        }   
+    }
+}
+
+# write config file
+sub config_write {
+    local($silent) = @_;
+
+    if (($changes || ! -e $config || !$config_read) || $silent) {
+        if (!$silent) {
+            if (-e $config) {
+                return 1 if 
+                    &confirm_yn("\nWrite your changes to $config?", "no");
+            } else {
+                return 1 unless
+            &confirm_yn("\nWrite your configuration to $config?", "yes");
+            }
+        }
+
+        local($dir) = &dirname($config);
+        if (! -d $dir && !&mkdirhier($dir)) {
+            warn "Cannot save your configuration\n";
+            return 0;
+        }
+
+        if(!open(C, "> $config")) {
+            warn "$config: $!\n"; return 0;
+        }
+        # prepare some variables
+        $send_message = "no" unless $send_message;
+        $defaultpasswd = "no" unless $defaultpasswd;
+        local($shpref) = "'" . join("', '", @shellpref) . "'";
+        local($shpath) = "'" . join("', '", @path) . "'";
+        local($user_var) = join('', @user_variable_list);
+
+        print C <<EOF;
+#
+# $config - automatic generated by adduser(8)
+#
+# Note: adduser read *and* write this file.
+#       You may change values, but don't add new things befor the
+#       line ``## DO NOT DELETE THIS LINE!''
+#
+
+# verbose = [0-2]
+verbose = $verbose
+
+# use password for new users
+# defaultpasswd =  yes | no
+defaultpasswd = $defaultpasswd
+
+# copy dotfiles from this dir ("/usr/share/skel" or "no")
+dotdir = "$dotdir"
+
+# send this file to new user ("/etc/adduser.message" or "no")
+send_message = "$send_message"
+
+# config file for adduser ("/etc/adduser.conf")
+config = "$config"
+
+# logfile ("/var/log/adduser" or "no")
+logfile = "$logfile"
+
+# default HOME directory ("/home")
+home = "$home"
+
+# List of directories where shells located
+# path = ('/bin', '/usr/bin', '/usr/local/bin')
+path = ($shpath)
+
+# common shell list, first element has higher priority
+# shellpref = ('bash', 'tcsh', 'ksh', 'csh', 'sh')
+shellpref = ($shpref)
+
+# defaultshell if not empty ("bash")
+defaultshell = "$defaultshell"
+
+# new users get this uid (1000)
+uid_start = 1000
+
+$do_not_delete
+## your own variables, see /etc/adduser.message
+## Warning: this may be a security hole!
+$user_var
+
+# end
+EOF
+        close C;
+    }
+}
+
 ################
 # main
 #
-&check_root;            # You must be root to run this script!
+$test = 0;              # test mode, only for development
+
+# init
+#&usage if $ARGV[0] =~ /^--?(help|h|\?)$/; # help if you are not root
+&check_root;            # you must be root to run this script!
 &variables;             # initialize variables
+&config_read(@ARGV);    # read variables form config-file
 &parse_arguments(@ARGV);    # parse arguments
 
+# 
+&copyright;
+&hints;
+
+# check
+$changes = 0;
 &passwd_check;          # check for valid passwdb
+&shells_read;           # read /etc/shells
 &passwd_read;           # read /etc/master.passwd
 &group_read;            # read /etc/group
 &group_check;           # check for incon*
-&dotdir_check;          # check $dotdir
-print "\n";
-&home_partition;        # find HOME partition
-&shells_read;           # read /etc/shells
-&shells_add;            # maybe add some new shells
-&shells_pref;           # enter default shell
-&password_pref;         # maybe use password
 
+# some questions
+&shells_add;                     # maybe add some new shells
+$defaultshell = &shell_default;  # enter default shell
+$home = &home_partition;         # find HOME partition
+$dotdir = &dotdir_default;       # check $dotdir
+$send_message = &message_default;   # send message to new user
+$defaultpasswd = &password_default; # maybe use password
+&config_write(0);                   # write variables in file
+
+# main loop for creating new users
 &new_users;             # add new users
 
 #end