# Functions for managing a domain's home directory
# setup_dir(&domain)
# Creates the home directory
sub setup_dir
{
local $tmpl = &get_template($_[0]->{'template'});
&require_useradmin();
local $qh = quotemeta($_[0]->{'home'});
# Get Unix user, either for this domain or its parent
local $uinfo;
if ($_[0]->{'unix'} || $_[0]->{'parent'}) {
local @users = &list_all_users();
($uinfo) = grep { $_->{'user'} eq $_[0]->{'user'} } @users;
}
if ($_[0]->{'unix'} && !$uinfo) {
# If we are going to have a Unix user but none has been created
# yet, fake his details here for use in chowning and skel copying
$uinfo ||= { 'uid' => $_[0]->{'uid'},
'gid' => $_[0]->{'ugid'},
'shell' => '/bin/sh',
'group' => $_[0]->{'group'} || $_[0]->{'ugroup'} };
}
# Create and populate home directory
&$first_print($text{'setup_home'});
&system_logged("mkdir $qh") if (!-d $_[0]->{'home'});
&system_logged("chmod '$uconfig{'homedir_perms'}' $qh");
if ($uinfo) {
&system_logged("chown $uinfo->{'uid'}:$uinfo->{'gid'} $qh");
}
if ($tmpl->{'skel'} ne "none" && !$_[0]->{'nocopyskel'}) {
©_skel_files(&substitute_domain_template($tmpl->{'skel'}, $_[0]),
$uinfo, $_[0]->{'home'},
$_[0]->{'group'} || $_[0]->{'ugroup'}, $_[0]);
}
# Setup sub-directories
local $d;
foreach $d (&virtual_server_directories($_[0])) {
&system_logged("mkdir -p $qh/$d->[0] 2>/dev/null");
&system_logged("chmod $d->[1] $qh/$d->[0]");
if ($uinfo) {
&system_logged("chown $uinfo->{'uid'}:$uinfo->{'gid'} $qh/$d->[0]");
}
}
&$second_print($text{'setup_done'});
return 1;
}
# modify_dir(&domain, &olddomain)
# Rename home directory if needed
sub modify_dir
{
if ($_[0]->{'home'} ne $_[1]->{'home'}) {
# Move the home directory if changed, and if not already moved as
# part of parent
if (-d $_[1]->{'home'}) {
&$first_print($text{'save_dirhome'});
local $out = &backquote_logged("mv ".quotemeta($_[1]->{'home'}).
" ".quotemeta($_[0]->{'home'})." 2>&1");
if ($?) {
&$second_print(&text('save_dirhomefailed', "$out"));
}
else {
&$second_print($text{'setup_done'});
}
}
}
if ($_[0]->{'unix'} && !$_[1]->{'unix'} ||
$_[0]->{'uid'} ne $_[1]->{'uid'}) {
# Unix user now exists or has changed! Set ownership of home dir
&$first_print($text{'save_dirchown'});
&set_home_ownership($_[0]);
&$second_print($text{'setup_done'});
}
}
# delete_dir(&domain)
# Delete the home directory
sub delete_dir
{
# Delete homedir
if (-d $_[0]->{'home'} && $_[0]->{'home'} ne "/") {
&$first_print($text{'delete_home'});
&system_logged("rm -rf ".quotemeta($_[0]->{'home'}));
&$second_print($text{'setup_done'});
}
}
# validate_dir(&domain)
# Returns an error message if the directory is missing, or has the wrong
# ownership
sub validate_dir
{
local ($d) = @_;
if (!-d $d->{'home'}) {
return &text('validate_edir', "$d->{'home'}");
}
local @st = stat($d->{'home'});
if ($d->{'uid'} && $st[4] != $d->{'uid'}) {
local $owner = getpwuid($st[4]);
return &text('validate_ediruser', "$d->{'home'}",
$owner, $d->{'user'})
}
if ($d->{'gid'} && $st[5] != $d->{'gid'} && $st[5] != $d->{'ugid'}) {
local $owner = getgrgid($st[5]);
return &text('validate_edirgroup', "$d->{'home'}",
$owner, $d->{'group'})
}
foreach my $sd (&virtual_server_directories($d)) {
if (!-d "$d->{'home'}/$sd->[0]") {
return &text('validate_esubdir', "$sd->[0]")
}
local @st = stat("$d->{'home'}/$sd->[0]");
if ($d->{'uid'} && $st[4] != $d->{'uid'}) {
local $owner = getpwuid($st[4]);
return &text('validate_esubdiruser', "$sd->[0]",
$owner, $d->{'user'})
}
if ($d->{'gid'} && $st[5] != $d->{'gid'} && $st[5] != $d->{'ugid'}) {
local $owner = getgrgid($st[5]);
return &text('validate_esubdirgroup', "$sd->[0]",
$owner, $d->{'group'})
}
}
return undef;
}
# check_dir_clash(&domain, [field])
sub check_dir_clash
{
# Does nothing ..?
return 0;
}
# backup_dir(&domain, file, &options, home-format)
# Backs up the server's home directory in tar format to the given file
sub backup_dir
{
&$first_print($text{'backup_dirtar'});
local $out;
local $cmd;
local $gzip = $_[3] && &has_command("gzip");
# Create exclude file
$xtemp = &transname();
&open_tempfile(XTEMP, ">$xtemp");
&print_tempfile(XTEMP, "domains\n");
&print_tempfile(XTEMP, "./domains\n");
if ($_[2]->{'dirnologs'}) {
&print_tempfile(XTEMP, "logs\n");
&print_tempfile(XTEMP, "./logs\n");
}
&print_tempfile(XTEMP, "virtualmin-backup\n");
&print_tempfile(XTEMP, "./virtualmin-backup\n");
foreach my $e (&get_backup_excludes($_[0])) {
&print_tempfile(XTEMP, "$e\n");
&print_tempfile(XTEMP, "./$e\n");
}
# Exclude all .zfs files, for Solaris
if ($gconfig{'os_type'} eq 'solaris') {
open(FIND, "find ".quotemeta($_[0]->{'home'})." -name .zfs |");
while() {
s/\r|\n//g;
s/^\Q$_[0]->{'home'}\E\///;
&print_tempfile(XTEMP, "$_\n");
&print_tempfile(XTEMP, "./$_\n");
}
close(FIND);
}
&close_tempfile(XTEMP);
# Do the backup
if ($_[3] && $config{'compression'} == 0) {
# With gzip
$cmd = "tar cfX - $xtemp . | gzip -c >".quotemeta($_[1]);
}
elsif ($_[3] && $config{'compression'} == 1) {
# With bzip
$cmd = "tar cfX - $xtemp . | bzip2 -c >".quotemeta($_[1]);
}
else {
# Plain tar
$cmd = "tar cfX ".quotemeta($_[1])." $xtemp .";
}
&execute_command("cd ".quotemeta($_[0]->{'home'})." && $cmd",
undef, \$out, \$out);
if ($?) {
&$second_print(&text('backup_dirtarfailed', "$out
"));
return 0;
}
else {
&$second_print($text{'setup_done'});
return 1;
}
}
# show_backup_dir(&options)
# Returns HTML for the backup logs option
sub show_backup_dir
{
return sprintf
"( %s)",
!$opts{'dirnologs'} ? "checked" : "", $text{'backup_dirlogs'};
}
# parse_backup_dir(&in)
# Parses the inputs for directory backup options
sub parse_backup_dir
{
local %in = %{$_[0]};
return { 'dirnologs' => !$in{'dir_logs'} };
}
# restore_dir(&domain, file, &options, homeformat?)
# Extracts the given tar file into server's home directory
sub restore_dir
{
&$first_print($text{'restore_dirtar'});
local $out;
local $cf = &compression_format($_[1]);
local $comp = $cf == 1 ? "gunzip -c" :
$cf == 2 ? "uncompress -c" :
$cf == 3 ? "bunzip2 -c" : "cat";
local $q = quotemeta($_[1]);
local $qh = quotemeta($_[0]->{'home'});
&execute_command("cd $qh && $comp $q | tar xf -", undef, \$out, \$out);
if ($?) {
&$second_print(&text('backup_dirtarfailed', "$out
"));
return 0;
}
else {
&$second_print($text{'setup_done'});
if ($_[0]->{'unix'}) {
# Set ownership on extracted home directory, apart from
# content of ~/homes
&$first_print($text{'restore_dirchowning'});
&set_home_ownership($_[0]);
&$second_print($text{'setup_done'});
}
return 1;
}
}
# set_home_ownership(&domain)
# Update the ownership of all files in a server's home directory, EXCEPT
# the homes directory which is used by mail users
sub set_home_ownership
{
local $hd = $config{'homes_dir'};
$hd =~ s/^\.\///;
local $gid = $_[0]->{'gid'} || $_[0]->{'ugid'};
&system_logged("find ".quotemeta($_[0]->{'home'})." | grep -v '$_[0]->{'home'}/$hd/' | sed -e 's/^/\"/' | sed -e 's/\$/\"/' | xargs chown $_[0]->{'uid'}:$gid");
&system_logged("chown $_[0]->{'uid'}:$gid ".quotemeta($_[0]->{'home'})."/".$config{'homes_dir'});
}
# virtual_server_directories(&dom)
# Returns a list of sub-directories that need to be created for virtual servers
sub virtual_server_directories
{
local $tmpl = &get_template($_[0]->{'template'});
local $perms = $tmpl->{'web_html_perms'};
return ( $d->{'subdom'} || $d->{'alias'} ? ( ) : ( [ &public_html_dir($_[0], 1), $perms ] ),
$d->{'subdom'} || $d->{'alias'} ? ( ) : ( [ 'cgi-bin', $perms ] ),
[ 'logs', '750' ],
[ $config{'homes_dir'}, '755' ] );
}
# create_server_tmp(&domain)
# Creates the temporary files directory for a domain, and returns the path
sub create_server_tmp
{
local ($d) = @_;
if ($d->{'dir'}) {
local $tmp = "$d->{'home'}/tmp";
if (!-d $tmp) {
&make_dir($tmp, 0750, 1);
&set_ownership_permissions($d->{'uid'}, $d->{'ugid'},
0750, $tmp);
}
return $tmp;
}
else {
# For domains without a home
return "/tmp";
}
}
# show_template_dir(&tmpl)
# Outputs HTML for editing directory-related template options
sub show_template_dir
{
local ($tmpl) = @_;
# The skeleton files directory
print &ui_table_row(&hlink($text{'tmpl_skel'}, "template_skel"),
&none_def_input("skel", $tmpl->{'skel'}, $text{'tmpl_skeldir'}, 0,
$tmpl->{'standard'} ? 1 : 0, undef,
[ "skel", "skel_subs" ])."\n".
&ui_textbox("skel", $tmpl->{'skel'} eq "none" ? undef
: $tmpl->{'skel'}, 40));
# Perform substitions on skel file contents
print &ui_table_row(&hlink($text{'tmpl_skel_subs'}, "template_skel_subs"),
&ui_yesno_radio("skel_subs", int($tmpl->{'skel_subs'})));
}
# parse_template_dir(&tmpl)
# Updates directory-related template options from %in
sub parse_template_dir
{
local ($tmpl) = @_;
# Save skeleton directory
$tmpl->{'skel'} = &parse_none_def("skel");
if ($in{"skel_mode"} == 2) {
-d $in{'skel'} || &error($text{'tmpl_eskel'});
$tmpl->{'skel_subs'} = $in{'skel_subs'};
}
}
$done_feature_script{'dir'} = 1;
1;