initial vpopmail support
[freeside.git] / bin / svc_acct.export
1 #!/usr/bin/perl -w
2 #
3 # $Id: svc_acct.export,v 1.20.2.1 2001-08-08 17:45:36 jeff Exp $
4 #
5 # Create and export password, radius and vpopmail password files:
6 # passwd, passwd.adjunct, shadow, acp_passwd, acp_userinfo, acp_dialup
7 # users/assign, domains/vdomain/vpasswd
8 # Also export sendmail and qmail config files.
9 #
10 #
11 # $Log: svc_acct.export,v $
12 # Revision 1.20.2.1  2001-08-08 17:45:36  jeff
13 # initial vpopmail support
14 #
15 #
16 #
17 #   Initial vpopmail changes
18 #
19 #
20
21 use strict;
22 use vars qw($conf);
23 use Archive::Tar;
24 use Fcntl qw(:flock);
25 use File::Path;
26 use IO::Handle;
27 use FS::Conf;
28 use Net::SSH qw(ssh);
29 use Net::SCP qw(scp);
30 use FS::UID qw(adminsuidsetup datasrc dbh);
31 use FS::Record qw(qsearch qsearchs fields);
32 use FS::svc_acct;
33 use FS::svc_domain;
34 use FS::svc_forward;
35
36 my $user = shift or die &usage;
37 adminsuidsetup $user;
38
39 $conf = new FS::Conf;
40
41 my $userpolicy = $conf->config('username_policy')
42   if $conf->exists('username_policy');
43
44 my @shellmachines = $conf->config('shellmachines')
45   if $conf->exists('shellmachines');
46
47 my @bsdshellmachines = $conf->config('bsdshellmachines')
48   if $conf->exists('bsdshellmachines');
49
50 my @nismachines = $conf->config('nismachines')
51   if $conf->exists('nismachines');
52
53 my @erpcdmachines = $conf->config('erpcdmachines')
54   if $conf->exists('erpcdmachines');
55
56 my @radiusmachines = $conf->config('radiusmachines')
57   if $conf->exists('radiusmachines');
58
59 my $icradiusmachines = $conf->exists('icradiusmachines');
60 my @icradiusmachines = $conf->config('icradiusmachines') if $icradiusmachines;
61 my $icradius_mysqldest =
62   $conf->config('icradius_mysqldest') || "/usr/local/var/"
63     if $icradiusmachines;
64 my $icradius_mysqlsource =
65   $conf->config('icradius_mysqlsource') || "/usr/local/var/freeside"
66     if $icradiusmachines;
67 my $icradius_dbh;
68 if ( $icradiusmachines && $conf->exists('icradius_secrets') ) {
69   $icradius_dbh = DBI->connect($conf->config('icradius_secrets'))
70     or die $DBI::errstr;;
71 } else {
72   $icradius_dbh = dbh;
73 }
74
75 my $textradiusprepend =
76   $conf->exists('textradiusprepend')
77     ? $conf->config('textradiusprepend')
78     : '';
79
80 my @vpopmailmachines = $conf->config('vpopmailmachines')
81   if $conf->exists('vpopmailmachines');
82
83 my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachines[0]);
84
85 my($shellmachine, @qmailmachines);
86 if ( $conf->exists('qmailmachines') ) {
87   $shellmachine = $conf->config('shellmachine');
88   @qmailmachines = $conf->config('qmailmachines');
89 }
90
91 my(@sendmailmachines, $sendmailconfigpath, $sendmailrestart);
92 if ( $conf->exists('sendmailmachines') ) {
93   @sendmailmachines = $conf->config('sendmailmachines');
94   $sendmailconfigpath = $conf->config('sendmailconfigpath') || '/etc';
95   $sendmailrestart = $conf->config('sendmailrestart');
96 }
97
98 my $mydomain = $conf->config('domain') if $conf->exists('domain');
99
100
101
102
103 my(@saltset)= ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
104 require 5.004; #srand(time|$$);
105
106 my $spooldir = "/usr/local/etc/freeside/export.". datasrc;
107 my $spoollock = "/usr/local/etc/freeside/svc_acct.export.lock.". datasrc;
108
109 open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!";
110 select(EXPORT); $|=1; select(STDOUT);
111 unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) {
112   seek(EXPORT,0,0);
113   my($pid)=<EXPORT>;
114   chop($pid);
115   #no reason to start lots of blocking processes
116   die "Is another export process running under pid $pid?\n";
117 }
118 seek(EXPORT,0,0);
119 print EXPORT $$,"\n";
120
121 my(@svc_domain)=qsearch('svc_domain',{});
122
123 ( open(MASTER,">$spooldir/master.passwd")
124   and flock(MASTER,LOCK_EX|LOCK_NB)  
125 ) or die "Can't open $spooldir/.master.passwd: $!";
126 ( open(PASSWD,">$spooldir/passwd")
127   and flock(PASSWD,LOCK_EX|LOCK_NB)  
128 ) or die "Can't open $spooldir/passwd: $!";
129 ( open(SHADOW,">$spooldir/shadow")
130   and flock(SHADOW,LOCK_EX|LOCK_NB)  
131 ) or die "Can't open $spooldir/shadow: $!";
132 ( open(ACP_PASSWD,">$spooldir/acp_passwd")
133   and flock(ACP_PASSWD,LOCK_EX|LOCK_NB)  
134 ) or die "Can't open $spooldir/acp_passwd: $!";
135 ( open(ACP_DIALUP,">$spooldir/acp_dialup")
136   and flock(ACP_DIALUP,LOCK_EX|LOCK_NB)  
137 ) or die "Can't open $spooldir/acp_dialup: $!";
138 ( open(USERS,">$spooldir/users")
139   and flock(USERS,LOCK_EX|LOCK_NB)  
140 ) or die "Can't open $spooldir/users: $!";
141
142 ( open(ASSIGN,">$spooldir/assign")
143   and flock(ASSIGN,LOCK_EX|LOCK_NB)  
144 ) or die "Can't open $spooldir/assign: $!";
145 ( open(RCPTHOSTS,">$spooldir/rcpthosts")
146   and flock(RCPTHOSTS,LOCK_EX|LOCK_NB) 
147 ) or die "Can't open $spooldir/rcpthosts: $!";
148 ( open(VPOPRCPTHOSTS,">$spooldir/vpoprcpthosts")
149   and flock(VPOPRCPTHOSTS,LOCK_EX|LOCK_NB) 
150 ) or die "Can't open $spooldir/rcpthosts: $!";
151 ( open(RECIPIENTMAP,">$spooldir/recipientmap") 
152   and flock(RECIPIENTMAP,LOCK_EX|LOCK_NB) 
153 ) or die "Can't open $spooldir/recipientmap: $!";
154 ( open(VIRTUALDOMAINS,">$spooldir/virtualdomains") 
155   and flock(VIRTUALDOMAINS,LOCK_EX|LOCK_NB)
156 ) or die "Can't open $spooldir/virtualdomains: $!";
157 ( open(VPOPVIRTUALDOMAINS,">$spooldir/vpopvirtualdomains") 
158   and flock(VPOPVIRTUALDOMAINS,LOCK_EX|LOCK_NB)
159 ) or die "Can't open $spooldir/virtualdomains: $!";
160 ( open(VIRTUSERTABLE,">$spooldir/virtusertable")
161   and flock(VIRTUSERTABLE,LOCK_EX|LOCK_NB)
162 ) or die "Can't open $spooldir/virtusertable: $!";
163 ( open(SENDMAIL_CW,">$spooldir/sendmail.cw")
164   and flock(SENDMAIL_CW,LOCK_EX|LOCK_NB)
165 ) or die "Can't open $spooldir/sendmail.cw: $!";
166
167
168
169 chmod 0644, "$spooldir/passwd",
170             "$spooldir/acp_dialup",
171             "$spooldir/assign",
172             "$spooldir/sendmail.cw",
173             "$spooldir/virtusertable",
174             "$spooldir/rcpthosts",
175             "$spooldir/vpoprcpthosts",
176             "$spooldir/recipientmap",
177             "$spooldir/virtualdomains",
178             "$spooldir/vpopvirtualdomains",
179
180 ;
181 chmod 0600, "$spooldir/master.passwd",
182             "$spooldir/acp_passwd",
183             "$spooldir/shadow",
184             "$spooldir/users",
185 ;
186
187 rmtree"$spooldir/domains", 0, 1;
188 mkdir "$spooldir/domains", 0700;
189
190 if ( $icradiusmachines ) {
191   my $sth = $icradius_dbh->prepare("DELETE FROM radcheck");
192   $sth->execute or die "Can't reset radcheck table: ". $sth->errstr;
193   my $sth2 = $icradius_dbh->prepare("DELETE FROM radreply");
194   $sth2->execute or die "Can't reset radreply table: ". $sth2->errstr;
195 }
196
197 setpriority(0,0,10);
198
199 my %usernames;  ## this hack helps keep the passwd files sane
200 my @sendmail;
201
202 my $svc_domain;
203 foreach $svc_domain (sort {$a->domain cmp $b->domain} @svc_domain) {
204
205   my($domain)=$svc_domain->domain;
206   print RCPTHOSTS "$domain\n.$domain\n";
207   print VPOPRCPTHOSTS "$domain\n";
208   print SENDMAIL_CW "$domain\n";
209
210   ###
211   # FORMAT OF THE ASSIGN/USERS FILE HERE
212   print ASSIGN join(":",
213     "+" . $domain . "-",
214     $domain,
215     $vpopuid,
216     $vpopgid,
217     $vpopdir . "/domains/" . $domain,
218     "-",
219     "",
220     "",
221   ), "\n";
222
223   (mkdir "$spooldir/domains/" . $domain, 0700)
224     or die "Can't create $spooldir/domains/" . $domain .": $!";
225
226   ( open(QMAILDEFAULT,">$spooldir/domains/" . $domain . "/.qmail-default")
227     and flock(QMAILDEFAULT,LOCK_EX|LOCK_NB)  
228   ) or die "Can't open $spooldir/domains/" . $domain . "/.qmail-default: $!";
229
230   ( open(VPASSWD,">$spooldir/domains/" . $domain . "/vpasswd")
231     and flock(VPASSWD,LOCK_EX|LOCK_NB)  
232   ) or die "Can't open $spooldir/domains/" . $domain . "/vpasswd: $!";
233
234   my ($svc_acct);
235
236   if ($svc_domain->catchall) {
237     $svc_acct = qsearchs('svc_acct', {'svcnum' => $svc_domain->catchall});
238     die "Cannot find catchall account for domain $domain\n" unless $svc_acct;
239
240     my $username = $svc_acct->username;
241     push @sendmail, "\@$domain\t$username\n";
242     print VIRTUALDOMAINS "$domain:$username-$domain\n",
243                          ".$domain:$username-$domain\n",
244     ;
245
246     ###
247     # FORMAT OF THE .QMAIL-DEFAULT FILE HERE
248     print QMAILDEFAULT "| $vpopdir/bin/vdelivermail \"\" $username\@$domain\n";
249
250   }else{
251     ###
252     # FORMAT OF THE .QMAIL-DEFAULT FILE HERE
253     print QMAILDEFAULT "| $vpopdir/bin/vdelivermail \"\" bounce-no-mailbox\n";
254   }
255
256   print VPOPVIRTUALDOMAINS "$domain:$domain\n";
257
258   foreach $svc_acct (qsearch('svc_acct', {'domsvc' => $svc_domain->svcnum})) {
259     my($password)=$svc_acct->getfield('_password');
260     my($cpassword,$rpassword);
261     if ( ( length($password) <= 8 )
262          && ( $password ne '*' )
263          && ( $password ne '' )
264        ) {
265       $cpassword=crypt($password,
266                        $saltset[int(rand(64))].$saltset[int(rand(64))]
267       );
268       $rpassword=$password;
269     } else {
270       $cpassword=$password;
271       $rpassword='UNIX';
272     }
273
274     my $username;
275
276     if ($mydomain && ($mydomain eq $svc_domain->domain)) {
277       $username=$svc_acct->username;
278     } elsif ($userpolicy =~ /^prepend domsvc$/) {
279       $username=$svc_acct->domsvc . $svc_acct->username;
280     } elsif ($userpolicy =~ /^append domsvc$/) {
281       $username=$svc_acct->username . $svc_acct->domsvc;
282     } elsif ($userpolicy =~ /^append domain$/) {
283       $username=$svc_acct->username . $svc_domain->domain;
284     } else {
285       die "Unknown policy in username_policy\n";
286     }
287
288     if ($svc_acct->dir ne '/dev/null' || $svc_acct->slipip ne '') {
289       if ($usernames{$username}++) {
290         die "Duplicate username detected: $username\n";
291       }
292     }
293             
294     if ( $svc_acct->uid  =~ /^(\d+)$/ ) {
295
296       die "Non-root user ". $svc_acct->username. " has 0 UID!"
297         if $svc_acct->uid == 0 && $svc_acct->username ne 'root';
298
299       if ( $svc_acct->dir ne "/dev/null") {
300
301         ###
302         # FORMAT OF FreeBSD MASTER PASSWD FILE HERE
303         print MASTER join(":",
304           $username,                        # User name
305           $cpassword,                       # Encrypted password
306           $svc_acct->uid,                   # User ID
307           $svc_acct->gid,                   # Group ID
308           "",                               # Login Class
309           "0",                              # Password Change Time
310           "0",                              # Password Expiration Time
311           $svc_acct->finger,                # Users name
312           $svc_acct->dir,                   # Users home directory
313           $svc_acct->shell,                 # shell
314         ), "\n" ;
315
316
317         ###
318         # FORMAT OF THE PASSWD FILE HERE
319         print PASSWD join(":",
320           $username,
321           'x', # "##". $username,
322           $svc_acct->uid,
323           $svc_acct->gid,
324           $svc_acct->finger,
325           $svc_acct->dir,
326           $svc_acct->shell,
327         ), "\n";
328
329         ###
330         # FORMAT OF THE SHADOW FILE HERE
331         print SHADOW join(":",
332           $username,
333           $cpassword,
334           '',
335           '',
336           '',
337           '',
338           '',
339           '',
340           '',
341         ), "\n";
342       }
343
344       ###
345       # FORMAT OF THE VPASSWD FILE HERE
346       print VPASSWD join(":",
347         $svc_acct->username,
348         $cpassword,
349         '1',
350         '0',
351         $svc_acct->username,
352         "$vpopdir/domains/" . $svc_domain->domain ."/" . $svc_acct->username,
353         'NOQUOTA',
354       ), "\n";
355
356     }
357
358     if ( $svc_acct->slipip ne '' ) {
359
360       ###
361       # FORMAT OF THE ACP_* FILES HERE
362       print ACP_PASSWD join(":",
363         $username,
364         $cpassword,
365         "0",
366         "0",
367         "",
368         "",
369         "",
370       ), "\n";
371
372       my($ip)=$svc_acct->slipip;
373
374       unless ( $ip eq '0.0.0.0' || $svc_acct->slipip eq '0e0' ) {
375         print ACP_DIALUP $username, "\t*\t", $svc_acct->slipip, "\n";
376       }
377
378       my %radreply = $svc_acct->radius_reply;
379       my %radcheck = $svc_acct->radius_check;
380
381       my $radcheck = join ", ", map { qq($_ = "$radcheck{$_}") } keys %radcheck;
382       $radcheck .= ", " if $radcheck;
383
384       ###
385       # FORMAT OF THE USERS FILE HERE
386       print USERS
387         $username,
388         qq(\t${textradiusprepend}),
389         $radcheck,
390         qq(Password = "$rpassword"\n\t),
391         join ",\n\t", map { qq($_ = "$radreply{$_}") } keys %radreply;
392
393       if ( $ip && $ip ne '0e0' ) {
394         #print USERS qq(,\n\tFramed-Address = "$ip"\n\n);
395         print USERS qq(,\n\tFramed-IP-Address = "$ip"\n\n);
396       } else {
397         print USERS qq(\n\n);
398       }
399
400       ###
401       # ICRADIUS export
402       if ( $icradiusmachines ) {
403   
404         my $sth = $icradius_dbh->prepare(
405           "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ".
406           join(", ", map { $icradius_dbh->quote( $_ ) } (
407             '',
408             $username,
409             "Password",
410             $svc_acct->_password,
411           ) ). " )"
412         );
413         $sth->execute or die "Can't insert into radcheck table: ". $sth->errstr;
414   
415         foreach my $attribute ( keys %radcheck ) {
416           my $sth = $icradius_dbh->prepare(
417             "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ".
418             join(", ", map { $icradius_dbh->quote( $_ ) } (
419               '',
420               $username,
421               $attribute,
422               $radcheck{$attribute},
423             ) ). " )"
424           );
425           $sth->execute or die "Can't insert into radcheck table: ". $sth->errstr;      }
426   
427         foreach my $attribute ( keys %radreply ) {
428           my $sth = $icradius_dbh->prepare(
429             "INSERT INTO radreply (id, UserName, Attribute, Value) VALUES ( ".
430             join(", ", map { $icradius_dbh->quote( $_ ) } (
431               '',
432               $username,
433               $attribute,
434               $radreply{$attribute},
435             ) ). " )"
436           );
437           $sth->execute or die "Can't insert into radreply table: ". $sth->errstr;      }
438       }
439     }
440   
441     ###
442     # vpopmail directory structure creation
443
444     (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username, 0700)
445       or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . ": $!";
446     (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir", 0700)
447       or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir: $!";
448     (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/cur", 0700)
449       or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/cur: $!";
450     (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/new", 0700)
451       or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/new: $!";
452     (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/tmp", 0700)
453       or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/tmp: $!";
454
455     ( open(DOTQMAIL,">$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/.qmail")
456       and flock(DOTQMAIL,LOCK_EX|LOCK_NB)  
457     ) or die "Can't open $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/.qmail: $!";
458
459     my($svc_forward);
460     foreach $svc_forward (qsearch('svc_forward', {'srcsvc' => $svc_acct->svcnum})) {
461       my($destination);
462       if ($svc_forward->dstsvc) {
463         my $dst_acct = qsearchs('svc_acct', {'svcnum' => $svc_forward->dstsvc});
464         my $dst_domain = qsearchs('svc_domain', {'svcnum' => $dst_acct->domsvc});
465         $destination = $dst_acct->username . '@' . $dst_domain->domain;
466
467         if ($dst_domain->domain eq $mydomain) {
468           print VIRTUSERTABLE $svc_acct->username . "@" . $svc_domain->domain .
469             "\t" . $dst_acct->username . "\n";
470           print RECIPIENTMAP $svc_acct->username . "@" . $svc_domain->domain .
471             ":$destination\n";
472         }
473       } else {
474         $destination = $svc_forward->dst;
475       }
476     
477       ###
478       # FORMAT OF .QMAIL FILES HERE
479       print DOTQMAIL "$destination\n";
480     }
481
482     flock(DOTQMAIL,LOCK_UN);
483     close DOTQMAIL;
484
485   }
486
487   flock(VPASSWD,LOCK_UN);
488   flock(QMAILDEFAULT,LOCK_UN);
489   close VPASSWD;
490   close QMAILDEFAULT;
491
492 }
493
494 ###
495 # FORMAT OF THE ASSIGN/USERS FILE FINAL LINE HERE
496 print ASSIGN ".\n";
497
498 print VIRTUSERTABLE @sendmail;
499
500 flock(MASTER,LOCK_UN);
501 flock(PASSWD,LOCK_UN);
502 flock(SHADOW,LOCK_UN);
503 flock(ACP_DIALUP,LOCK_UN);
504 flock(ACP_PASSWD,LOCK_UN);
505 flock(USERS,LOCK_UN);
506 flock(ASSIGN,LOCK_UN);
507 flock(SENDMAIL_CW,LOCK_UN);
508 flock(VIRTUSERTABLE,LOCK_UN);
509 flock(RCPTHOSTS,LOCK_UN);
510 flock(VPOPRCPTHOSTS,LOCK_UN);
511 flock(RECIPIENTMAP,LOCK_UN);
512 flock(VPOPVIRTUALDOMAINS,LOCK_UN);
513
514 close MASTER;
515 close PASSWD;
516 close SHADOW;
517 close ACP_DIALUP;
518 close ACP_PASSWD;
519 close USERS;
520 close ASSIGN;
521 close SENDMAIL_CW;
522 close VIRTUSERTABLE;
523 close RCPTHOSTS;
524 close VPOPRCPTHOSTS;
525 close RECIPIENTMAP;
526 close VPOPVIRTUALDOMAINS;
527
528 ###
529 # export stuff
530 #
531
532 my($ashellmachine);
533 foreach $ashellmachine (@shellmachines) {
534   my $scp = new Net::SCP;
535   $scp->scp("$spooldir/passwd","root\@$ashellmachine:/etc/passwd.new")
536     or die "scp error: ". $scp->{errstr};
537   $scp->scp("$spooldir/shadow","root\@$ashellmachine:/etc/shadow.new")
538     or die "scp error: ". $scp->{errstr};
539   ssh("root\@$ashellmachine",
540     "( ".
541       "mv /etc/passwd.new /etc/passwd; ".
542       "mv /etc/shadow.new /etc/shadow; ".
543     " )"
544   )
545     == 0 or die "ssh error: $!";
546 }
547
548 my($bsdshellmachine);
549 foreach $bsdshellmachine (@bsdshellmachines) {
550   my $scp = new Net::SCP;
551   $scp->scp("$spooldir/passwd","root\@$bsdshellmachine:/etc/passwd.new")
552     or die "scp error: ". $scp->{errstr};
553   $scp->scp("$spooldir/master.passwd","root\@$bsdshellmachine:/etc/master.passwd.new")
554     or die "scp error: ". $scp->{errstr};
555   ssh("root\@$bsdshellmachine",
556     "( ".
557       "mv /etc/passwd.new /etc/passwd; ".
558       "mv /etc/master.passwd.new /etc/master.passwd; ".
559     " )"
560   )
561     == 0 or die "ssh error: $!";
562 }
563
564 my($nismachine);
565 foreach $nismachine (@nismachines) {
566   my $scp = new Net::SCP;
567   $scp->scp("$spooldir/passwd","root\@$nismachine:/etc/global/passwd")
568     or die "scp error: ". $scp->{errstr};
569   $scp->scp("$spooldir/shadow","root\@$nismachine:/etc/global/shadow")
570     or die "scp error: ". $scp->{errstr};
571   ssh("root\@$nismachine",
572     "( ".
573       "cd /var/yp; make; ".
574     " )"
575   )
576     == 0 or die "ssh error: $!";
577 }
578
579 my($erpcdmachine);
580 foreach $erpcdmachine (@erpcdmachines) {
581   my $scp = new Net::SCP;
582   $scp->scp("$spooldir/acp_passwd","root\@$erpcdmachine:/usr/annex/acp_passwd")
583     or die "scp error: ". $scp->{errstr};
584   $scp->scp("$spooldir/acp_dialup","root\@$erpcdmachine:/usr/annex/acp_dialup")
585     or die "scp error: ". $scp->{errstr};
586   ssh("root\@$erpcdmachine",
587     "( ".
588       "kill -USR1 \`cat /usr/annex/erpcd.pid\'".
589     " )"
590   )
591     == 0 or die "ssh error: $!";
592 }
593
594 my($radiusmachine);
595 foreach $radiusmachine (@radiusmachines) {
596   my $scp = new Net::SCP;
597   $scp->scp("$spooldir/users","root\@$radiusmachine:/etc/raddb/users")
598     or die "scp error: ". $scp->{errstr};
599   ssh("root\@$radiusmachine",
600     "( ".
601       "builddbm".
602     " )"
603   )
604     == 0 or die "ssh error: $!";
605 }
606
607 foreach my $icradiusmachine ( @icradiusmachines ) {
608   my( $machine, $db, $user, $pass ) = split(/\s+/, $icradiusmachine);
609   chdir $icradius_mysqlsource or die "Can't cd $icradius_mysqlsource: $!";
610   open(WRITER,"|ssh root\@$machine mysql -v --user=$user -p $db");
611   my $oldfh = select WRITER; $|=1; select $oldfh;
612   print WRITER "$pass\n";
613   sleep 2;
614   print WRITER "LOCK TABLES radcheck WRITE, radreply WRITE;\n";
615   foreach my $file ( glob("radcheck.*") ) {
616     my $scp = new Net::SCP;
617     $scp->scp($file,"root\@$machine:$icradius_mysqldest/$db/$file")
618       or die "scp error: ". $scp->{errstr};
619   }
620   foreach my $file ( glob("radreply.*") ) {
621     my $scp = new Net::SCP;
622     $scp->scp($file,"root\@$machine:$icradius_mysqldest/$db/$file")
623       or die "scp error: ". $scp->{errstr};
624   }
625   close WRITER;
626 }
627
628 my @args = ("/bin/tar", "c", "--force-local", "-C", "$spooldir", "-f", "$spooldir/vpoptarball", "domains");
629
630 system {$args[0]} @args;
631
632 my($vpopmailmachine);
633 foreach $vpopmailmachine (@vpopmailmachines) {
634   my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachine);
635   my $scp = new Net::SCP;
636   $scp->scp("$spooldir/vpoptarball","root\@$machine:vpoptarball")
637     or die "scp error: ". $scp->{errstr};
638   ssh("root\@$machine",
639     "( ".
640       "tar xf vpoptarball; ".
641       "chown -R $vpopuid:$vpopgid domains; ".
642       "tar cf vpoptarball domains; ".
643       "cd $vpopdir; ".
644       "tar xf ~/vpoptarball; ".
645     " )"
646   )
647     == 0 or die "ssh error: $!";
648
649   $scp->scp("$spooldir/assign","root\@$machine:/var/qmail/users/assign")
650     or die "scp error: ". $scp->{errstr};
651   $scp->scp("$spooldir/vpopvirtualdomains","root\@$machine:/var/qmail/control/virtualdomains")
652     or die "scp error: ". $scp->{errstr};
653   $scp->scp("$spooldir/vpoprcpthosts","root\@$machine:/var/qmail/control/rcpthosts")
654     or die "scp error: ". $scp->{errstr};
655 }
656
657 my($sendmailmachine);
658 foreach $sendmailmachine (@sendmailmachines) {
659   my $scp = new Net::SCP;
660   $scp->scp("$spooldir/sendmail.cw","root\@$sendmailmachine:$sendmailconfigpath/sendmail.cw.new")
661     or die "scp error: ". $scp->{errstr};
662   $scp->scp("$spooldir/virtusertable","root\@$sendmailmachine:$sendmailconfigpath/virtusertable.new")
663     or die "scp error: ". $scp->{errstr};
664   ssh("root\@$sendmailmachine",
665     "( ".
666       "mv $sendmailconfigpath/sendmail.cw.new $sendmailconfigpath/sendmail.cw; ".
667       "mv $sendmailconfigpath/virtusertable.new $sendmailconfigpath/virtusertable; ".
668       $sendmailrestart.
669     " )"
670   )
671     == 0 or die "ssh error: $!";
672 }
673
674 my($qmailmachine);
675 foreach $qmailmachine (@qmailmachines) {
676   my $scp = new Net::SCP;
677   $scp->scp("$spooldir/recipientmap","root\@$qmailmachine:/var/qmail/control/recipientmap")
678     or die "scp error: ". $scp->{errstr};
679   $scp->scp("$spooldir/virtualdomains","root\@$qmailmachine:/var/qmail/control/virtualdomains")
680     or die "scp error: ". $scp->{errstr};
681   $scp->scp("$spooldir/rcpthosts","root\@$qmailmachine:/var/qmail/control/rcpthosts")
682     or die "scp error: ". $scp->{errstr};
683   #ssh("root\@$qmailmachine","/etc/init.d/qmail restart")
684   #  == 0 or die "ssh error: $!";
685 }
686
687 unlink $spoollock;
688 flock(EXPORT,LOCK_UN);
689 close EXPORT;
690
691 #
692
693 sub usage {
694   die "Usage:\n\n  svc_acct.export user\n";
695 }
696