#!/usr/bin/perl -w # # EMULAB-COPYRIGHT # Copyright (c) 2000-2007 University of Utah and the Flux Group. # All rights reserved. # # # WARNING: See db/genelists.in for clause testing for protouser names. # use English; use strict; use Getopt::Std; use lib '@prefix@/lib'; use libdb; use libtestbed; use User; my $tbadmin = '@TBADMINGROUP@'; my $ELABINELAB = @ELABINELAB@; my $MINUID = @MIN_UNIX_UID@; my $MINGID = @MIN_UNIX_GID@; my $wap = '@prefix@/sbin/withadminprivs'; my $mkproj = '@prefix@/sbin/mkproj'; my $mkgroup = '@prefix@/sbin/mkgroup'; my $mkacct = '@prefix@/sbin/tbacct'; my $modgrps = '@prefix@/sbin/modgroups'; my $protouser = 'elabman'; my $protouser_name = 'Emulab Manager'; my $protouser_email = '@TBOPSEMAIL@'; my $protouser_shell = 'tcsh'; my $protouser_notes = "DO NOT DELETE THIS ACCOUNT!"; my $HOMEDIR = USERROOT(); my $protoproj = 'emulab-ops'; my $protoproj_desc = 'Operations Meta-Project'; my $batchmode = 0; my $pid_idx = 1; # Initial IDX for protoproj. my $trust = "project_root"; my $binshell = "/bin/nologin"; my $uid_uuid; my $gid_uuid; my $password; my $encpass; my %opts; my $mailman_password; my $CONTROL = "@USERNODE@"; my $BOSSNODE = "@BOSSNODE@"; # # Handle command-line options # sub usage { print "Usage: firstuser [-b] [-p password] [-u user] [-n name] [-e email]\n"; exit(1); } if (! getopts("bp:u:n:e:", \%opts)) { usage(); } if (defined($opts{b})) { $batchmode = 1; } if (defined($opts{p})) { $password = $opts{p}; } if (defined($opts{u})) { $protouser = $opts{u}; $trust = "local_root"; } if (defined($opts{n})) { $protouser_name = $opts{n}; } if (defined($opts{e})) { $protouser_email = $opts{e}; } my $user = User->Lookup('$protouser'); if (defined($user)) { die("This script has already been run, no need to run it again!\n"); } if ($UID != 0) { die "Sorry, this must be run as root\n"; } if (!defined($opts{u})) { print "This script will create the 'proto-user' $protouser, which you will\n"; print "use to bootstrap other users. It also creates the emulab-ops\n"; print "meta-project.\n\n"; } # Need this for mailman support; harmless. $mailman_password = substr(TBGenSecretKey(), 0, 10); # Get a password for the user if (!defined($password)) { # # In batch mode, we get it from the root user password entry. # Otherwise prompt user for it. # if ($batchmode) { (undef,$encpass) = getpwnam("root") or die("No root user!\n"); } else { print "Pick a password for $protouser (warning, will be echoed): "; $password = <>; chomp($password); } } if (!defined($encpass)) { my @salt_chars = ('a'..'z','A'..'Z','0'..'9'); my $salt = $salt_chars[rand(@salt_chars)] . $salt_chars[rand(@salt_chars)]; $encpass = crypt($password, "\$1\$${salt}"); } # Get uid for the user and a gid for the project # For non-initial users, get the next available index. my $result = DBQueryFatal("select idx from emulab_indicies ". "where name='next_uid'"); if ($result->numrows() > 0) { ($MINUID) = $result->fetchrow_array(); } my $uid = $MINUID; while (getpwuid($uid)) { $uid++; } my $gid = $MINGID; while (getgrgid($gid)) { $gid++; } # We put the proto-user in the tbadmin group, because the emulab-ops # group does not exist yet my $agid = (getgrnam($tbadmin))[2]; if (!defined $agid) { die "Unable to get group ID for $tbadmin\n"; } # And in the wheel group so elabman can sudo to root. my $Ggid = "wheel"; if (defined($opts{u})) { $Ggid .= " $protoproj"; } if (!$batchmode) { print "Creating user/project: Are you sure? (Y/N) "; if (<> !~ /Y/i) { die "Aborted\n"; } } # Initial protouser gets a real shell until actively frozen later. # Also setup a notes entry. if (!defined($opts{u})) { $binshell = "/bin/tcsh"; $protouser_notes = "This account can be frozen after your Emulab ". "is fully setup and running. DO NOT DELETE THIS ACCOUNT!"; } print "Creating user on boss...\n"; if (system("/usr/sbin/pw useradd $protouser -u $uid -g $agid ". "-G \"$Ggid\" -h - " . "-m -d $HOMEDIR/$protouser -s $binshell ". "-c \"$protouser_name\"")) { die "Unable to add user to the password file!\n"; } if ($CONTROL ne $BOSSNODE) { print "Creating user on ops...\n"; if (system("ssh $CONTROL ". "'/usr/sbin/pw useradd $protouser -u $uid -g $agid ". "-G \"$Ggid\" -h - -d $HOMEDIR/$protouser -s $binshell ". "-c \"$protouser_name\"'")) { die "Unable to add user to the ops password file!\n"; } } # These users do not really need a uuid, but give them one anyway. $uid_uuid = NewUUID(); # Initialize the index value; DBQueryFatal("replace into emulab_indicies set name='next_uid',idx=$uid+1"); print "Creating user in database...\n"; DBQueryFatal("insert into users set uid='$protouser', usr_created=now(), " . "usr_name='$protouser_name', uid_uuid='$uid_uuid', ". "usr_addr='DO NOT DELETE THIS ACCOUNT', ". "usr_pswd='$encpass', unix_uid=$uid, notes='$protouser_notes', ". "usr_modified=now(), admin=1, webonly=0, status='active',". "usr_shell='$protouser_shell', usr_email='$protouser_email', ". "mailman_password='$mailman_password',uid_idx=$uid"); DBQueryFatal("insert into user_stats set ". " uid='$protouser', uid_idx=$uid, uid_uuid='$uid_uuid'"); if (!defined($opts{u})) { $gid_uuid = NewUUID(); print "Creating project in database...\n"; DBQueryFatal("insert into projects set pid='$protoproj', created=now(), " . "name='$protoproj_desc', head_uid='$protouser', ". "head_idx=$uid, unix_gid=$gid,approved=1,pid_idx=$pid_idx"); DBQueryFatal("insert into project_stats set ". "pid='$protoproj',pid_idx=$pid_idx"); print "Creating group in database...\n"; DBQueryFatal("insert into groups set pid='$protoproj', gid='$protoproj', ". "leader='$protouser', leader_idx=$uid, created=now(), ". "description='Default Group', " . "unix_gid=$gid, gid_idx=$pid_idx, pid_idx=$pid_idx, ". "unix_name='$protoproj', gid_uuid='$gid_uuid'"); DBQueryFatal("insert into group_stats set ". " pid='$protoproj',gid='$protoproj', ". " gid_idx=$pid_idx, gid_uuid='$gid_uuid'"); DBQueryFatal("replace into emulab_indicies set name='next_gid',idx=10000"); } # Flip UID to the new user so that the mk* scripts are happy - they can't # be run as root for accountability reasons $EUID = $UID = $uid; $EGID = $GID = $gid; if (defined($opts{u})) { # # Creating specific user; it is implied that the user will go into # the proto project, which must already exist of course. # print "Running mkacct...\n"; if (system "$wap $mkacct -b add $protouser") { die "Unable to create account for initial user $protouser\n"; } print "Running modgroups...\n"; if (system "$wap $modgrps -a $protoproj:$protoproj:$trust $protouser") { die "Unable to add initial user $protouser to project $protoproj\n"; } } else { # # Creating the initial project. This handles everything. # print "Running mkproj...\n"; if (system "$wap $mkproj $protoproj") { die "Unable to create initial project $protoproj\n"; } } if (defined($opts{u})) { exit(0); } # # Okay, if not ELABINELAB, then set the firstinitstate so that the web # interface will take the user through his first project setup. # if (!$ELABINELAB) { TBSetSiteVar("general/firstinit/state", "createproject"); } print "User created. Once the web page is up, you should be able to log in\n"; print "as '$protouser' with the password you just entered. Refer to\n"; print "setup-db.txt for instructions on creating a 'real' user account for\n"; print "yourself.\n";