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