Enable usermin boot with insserv
[webmin.git] / makedebian.pl
1 #!/usr/local/bin/perl
2 # Build a Debian package of Webmin
3
4 use POSIX;
5
6 if ($ARGV[0] eq "--webmail" || $ARGV[0] eq "-webmail") {
7         $webmail = 1;
8         shift(@ARGV);
9         }
10 if ($0 =~ /useradmin|usermin/ || `pwd` =~ /useradmin|usermin/) {
11         if ($webmail) {
12                 $product = "usermin-webmail";
13                 }
14         else {
15                 $product = "usermin";
16                 }
17         $baseproduct = "usermin";
18         $port = 20000;
19         }
20 else {
21         $product = "webmin";
22         $baseproduct = "webmin";
23         $port = 10000;
24         }
25 $ucproduct = ucfirst($baseproduct);
26 $tmp_dir = "/tmp/debian";
27 $debian_dir = "$tmp_dir/DEBIAN";
28 $control_file = "$debian_dir/control";
29 $usr_dir = "$tmp_dir/usr/share/$baseproduct";
30 $pam_dir = "$tmp_dir/etc/pam.d";
31 $init_dir = "$tmp_dir/etc/init.d";
32 @rc_dirs = ( "$tmp_dir/etc/rc2.d", "$tmp_dir/etc/rc3.d", "$tmp_dir/etc/rc5.d" );
33 $pam_file = "$pam_dir/$baseproduct";
34 $preinstall_file = "$debian_dir/preinst";
35 $postinstall_file = "$debian_dir/postinst";
36 $preuninstall_file = "$debian_dir/prerm";
37 $postuninstall_file = "$debian_dir/postrm";
38 $copyright_file = "$debian_dir/copyright";
39 $changelog_file = "$debian_dir/changelog";
40 $conffiles_file = "$debian_dir/conffiles";
41
42 -d "tarballs" || die "makedebian.pl must be run in the $ucproduct root directory";
43 -r "/etc/debian_version" || die "makedebian.pl must be run on Debian";
44 chop($webmin_dir = `pwd`);
45
46 @ARGV == 1 || @ARGV == 2 ||
47         die "usage: makedebian.pl [--webmail] <version> [release]";
48 $ver = $ARGV[0];
49 if ($ARGV[1]) {
50         $rel = "-".$ARGV[1];
51         }
52 -r "tarballs/$product-$ver.tar.gz" || die "tarballs/$product-$ver.tar.gz not found";
53
54 # Create the base directories
55 print "Creating Debian package of ",ucfirst($product)," ",$ver,$rel," ...\n";
56 system("rm -rf $tmp_dir");
57 mkdir($tmp_dir, 0755);
58 chmod(0755, $tmp_dir);
59 mkdir($debian_dir, 0755);
60 system("mkdir -p $pam_dir");
61 system("mkdir -p $init_dir");
62 foreach $d (@rc_dirs) {
63         system("mkdir -p $d");
64         }
65 system("mkdir -p $usr_dir");
66
67 # Un-tar the package to the correct locations
68 system("gunzip -c tarballs/$product-$ver.tar.gz | (cd $tmp_dir ; tar xf -)") &&
69         die "un-tar failed!";
70 system("mv $tmp_dir/$product-$ver/* $usr_dir");
71 rmdir("$tmp_dir/$product-$ver");
72 system("mv $usr_dir/$baseproduct-debian-pam $pam_file");
73 system("cd $usr_dir && (find . -name '*.cgi' ; find . -name '*.pl') | perl perlpath.pl /usr/bin/perl -");
74 system("cd $usr_dir && rm -f mount/freebsd-mounts*");
75 system("cd $usr_dir && rm -f mount/openbsd-mounts*");
76 if ($product eq "webmin") {
77         system("cd $usr_dir && rm -f mount/macos-mounts*");
78         system("cd $usr_dir && rm -f webmin-gentoo-init");
79         system("cd $usr_dir && rm -rf format bsdexports hpuxexports sgiexports zones rbac");
80         system("cd $usr_dir && rm -f acl/Authen-SolarisRBAC-0.1*");
81         }
82
83 # Create init script
84 system("mv $usr_dir/$baseproduct-init $init_dir/$baseproduct");
85 foreach $d (@rc_dirs) {
86         system("ln -s ../init.d/$baseproduct $d/S99$baseproduct");
87         }
88 system("echo deb >$usr_dir/install-type");
89 system("echo $product >$usr_dir/deb-name");
90 system("cd $usr_dir && chmod -R og-w .");
91 if ($< == 0) {
92         system("cd $usr_dir && chown -R root:bin .");
93         }
94 $size = int(`du -sk $tmp_dir`);
95
96 # Create the control file
97 open(CONTROL, ">$control_file");
98 print CONTROL <<EOF;
99 Package: $product
100 Version: $ver$rel
101 Section: admin
102 Priority: optional
103 Architecture: all
104 Depends: bash, perl, libnet-ssleay-perl, openssl, libauthen-pam-perl, libpam-runtime, libio-pty-perl, apt-show-versions
105 Pre-Depends: bash, perl
106 Installed-Size: $size
107 Maintainer: Jamie Cameron <jcameron\@webmin.com>
108 Provides: $baseproduct
109 EOF
110 if ($product eq "webmin") {
111         print CONTROL <<EOF;
112 Replaces: webmin-adsl, webmin-apache, webmin-bandwidth, webmin-bind, webmin-burner, webmin-cfengine, webmin-cluster, webmin-core, webmin-cpan, webmin-dhcpd, webmin-exim, webmin-exports, webmin-fetchmail, webmin-firewall, webmin-freeswan, webmin-frox, webmin-fsdump, webmin-grub, webmin-heartbeat, webmin-htaccess, webmin-inetd, webmin-jabber, webmin-ldap-netgroups, webmin-ldap-user-simple, webmin-ldap-useradmin, webmin-lilo, webmin-logrotate, webmin-lpadmin, webmin-lvm, webmin-mailboxes, webmin-mon, webmin-mysql, webmin-nis, webmin-openslp, webmin-postfix, webmin-postgresql, webmin-ppp, webmin-pptp-client, webmin-pptp-server, webmin-procmail, webmin-proftpd, webmin-pserver, webmin-quota, webmin-samba, webmin-sarg, webmin-sendmail, webmin-shorewall, webmin-slbackup, webmin-smart-status, webmin-snort, webmin-software, webmin-spamassassin, webmin-squid, webmin-sshd, webmin-status, webmin-stunnel, webmin-updown, webmin-usermin, webmin-vgetty, webmin-webalizer, webmin-wuftpd, webmin-wvdial, webmin-xinetd
113 Description: web-based administration interface for Unix systems
114   Using Webmin you can configure DNS, Samba, NFS, local/remote filesystems
115   and more using your web browser.  After installation, enter the URL
116   https://localhost:10000/ into your browser and login as root with your root
117   password.
118 EOF
119         }
120 else {
121         print CONTROL <<EOF;
122 Replaces: usermin-at, usermin-changepass, usermin-chfn, usermin-commands, usermin-cron, usermin-cshrc, usermin-fetchmail, usermin-forward, usermin-gnupg, usermin-htaccess, usermin-htpasswd, usermin-mailbox, usermin-man, usermin-mysql, usermin-plan, usermin-postgresql, usermin-proc, usermin-procmail, usermin-quota, usermin-schedule, usermin-shell, usermin-spamassassin, usermin-ssh, usermin-tunnel, usermin-updown, usermin-usermount
123 Description: web-based user account administration interface for Unix systems
124   After installation, enter the URL http://localhost:20000/ into your browser
125   and login as any user on your system.
126 EOF
127         }
128 close(CONTROL);
129
130 # Create the copyright file
131 $nowstr = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));
132 open(COPY, ">$copyright_file");
133 print COPY <<EOF;
134 This package was debianized by Jamie Cameron <jcameron\@webmin.com> on
135 $nowstr.
136
137 It was downloaded from: http://www.webmin.com/
138
139 Upstream author: Jamie Cameron <jcameron\@webmin.com>
140
141 Copyright:
142
143 EOF
144 open(BSD, "$usr_dir/LICENCE");
145 while(<BSD>) {
146         print COPY $_;
147         }
148 close(BSD);
149 close(COPY);
150
151 # Create the config files file, for those we don't want to replace
152 open(CONF, ">$conffiles_file");
153 print CONF "/etc/pam.d/$baseproduct\n";
154 close(CONF);
155
156 # Get the changes for each module and version
157 $changes = { };
158 foreach $f (sort { $a cmp $b } ( glob("*/CHANGELOG"), "CHANGELOG" )) {
159         # Get the module name and type
160         local $mod = $f =~ /^(\S+)\/CHANGELOG/ ? $1 : "core";
161         next if ($mod ne "core" && -l $mod);
162         local $desc;
163         if ($mod eq "core") {
164                 $desc = "$ucproduct Core";
165                 }
166         else {
167                 local $m = $f;
168                 local %minfo;
169                 $m =~ s/CHANGELOG/module.info/;
170                 &read_file($m, \%minfo);
171                 next if (!$minfo{'longdesc'});
172                 $desc = $minfo{'desc'};
173                 }
174
175         # Read its change log file
176         local $inversion;
177         open(LOG, $f);
178         while(<LOG>) {
179                 s/\r|\n//g;
180                 if (/^----\s+Changes\s+since\s+(\S+)\s+----/) {
181                         $inversion = $1;
182                         }
183                 elsif ($inversion && /\S/) {
184                         push(@{$changes->{$inversion}->{$desc}}, $_);
185                         }
186                 }
187         }
188
189 # Create the changelog file from actual changes, plus the historical changelog
190 open(CHANGELOG, ">$changelog_file");
191 foreach $v (sort { $a <=> $b } (keys %$changes)) {
192         if ($ver > $v && sprintf("%.2f0", $ver) == $v) {
193                 $forv = $ver;
194                 }
195         else {
196                 $forv = sprintf("%.2f0", $v+0.01);
197                 }
198         @st = stat("tarballs/webmin-$forv.tar.gz");
199         $vtimestr = strftime("%a, %d %b %Y %H:%M:%S %z", localtime($st[9]));
200         print CHANGELOG "$baseproduct ($forv) stable; urgency=low\n";
201         print CHANGELOG "\n";
202         foreach $desc (keys %{$changes->{$v}}) {
203                 foreach $c (@{$changes->{$v}->{$desc}}) {
204                         @lines = &wrap_lines("$desc : $c", 65);
205                         print CHANGELOG " * $lines[0]\n";
206                         foreach $l (@lines[1 .. $#lines]) {
207                                 print CHANGELOG "   $l\n";
208                                 }
209                         }
210                 }
211         print CHANGELOG "\n";
212         print CHANGELOG "-- Jamie Cameron <jcameron\@webmin.com> $vtimestr\n";
213         print CHANGELOG "\n";
214         }
215 close(CHANGELOG);
216
217 # Get the temp-directory creator script
218 open(TEMP, "maketemp.pl");
219 while(<TEMP>) {
220         $maketemp .= $_;
221         }
222 close(TEMP);
223 $maketemp =~ s/\\/\\\\/g;
224 $maketemp =~ s/`/\\`/g;
225 $maketemp =~ s/\$/\\\$/g;
226
227 # Create the pre-install script
228 # No need for an OS check, as all debians are supported.
229 open(SCRIPT, ">$preinstall_file");
230 print SCRIPT <<EOF;
231 #!/bin/sh
232 perl <<EOD;
233 $maketemp
234 EOD
235 if [ "\$1" != "upgrade" ]; then
236         if [ "\$WEBMIN_PORT\" != \"\" ]; then
237                 port=\$WEBMIN_PORT
238         else
239                 port=$port
240         fi
241         perl -e 'use Socket; socket(FOO, PF_INET, SOCK_STREAM, getprotobyname("tcp")); setsockopt(FOO, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)); bind(FOO, pack_sockaddr_in(\$ARGV[0], INADDR_ANY)) || exit(1); exit(0);' \$port
242         if [ "\$?" != "0" ]; then
243                 echo Port \$port is already in use
244                 exit 2
245         fi
246 fi
247 EOF
248 close(SCRIPT);
249 system("chmod 755 $preinstall_file");
250
251 # Create the post-install script
252 open(SCRIPT, ">$postinstall_file");
253 print SCRIPT <<EOF;
254 #!/bin/sh
255 inetd=`grep "^inetd=" /etc/$baseproduct/miniserv.conf 2>/dev/null | sed -e 's/inetd=//g'`
256 if [ "\$1" = "upgrade" ]; then
257         # Upgrading the package, so stop the old webmin properly
258         if [ "\$inetd" != "1" ]; then
259                 /etc/init.d/$baseproduct stop >/dev/null 2>&1 </dev/null
260         fi
261 fi
262 cd /usr/share/$baseproduct
263 config_dir=/etc/$baseproduct
264 var_dir=/var/$baseproduct
265 perl=/usr/bin/perl
266 autoos=3
267 if [ "\$WEBMIN_PORT\" != \"\" ]; then
268         port=\$WEBMIN_PORT
269 else
270         port=$port
271 fi
272 login=root
273 if [ -r /etc/shadow ]; then
274         crypt=x
275 else
276         crypt=`grep "^root:" /etc/passwd | cut -f 2 -d :`
277 fi
278 host=`hostname`
279 ssl=1
280 atboot=1
281 nochown=1
282 autothird=1
283 noperlpath=1
284 nouninstall=1
285 nostart=1
286 export config_dir var_dir perl autoos port login crypt host ssl nochown autothird noperlpath nouninstall nostart allow atboot
287 tempdir=/tmp/.webmin
288 if [ ! -d \$tempdir ]; then
289         tempdir=/tmp
290 fi
291 ./setup.sh >$tempdir/$product-setup.out 2>&1
292 if [ "$product" = "webmin" ]; then
293         grep sudo= /etc/$product/miniserv.conf >/dev/null 2>&1
294         if [ "\$?" = 1 ]; then
295                 # Allow sudo-based logins for Ubuntu
296                 echo sudo=1 >>/etc/$product/miniserv.conf
297         fi
298 fi
299 rm -f /var/lock/subsys/$baseproduct
300 if [ "$inetd" != "1" ]; then
301         if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
302                 invoke-rc.d $baseproduct start >/dev/null 2>&1 </dev/null
303         else
304                 /etc/init.d/$baseproduct start >/dev/null 2>&1 </dev/null
305         fi
306 fi
307 if [ "$product" = "usermin" ]; then
308         insserv $baseproduct >/dev/null 2>&1
309 fi
310 cat >/etc/$baseproduct/uninstall.sh <<EOFF
311 #!/bin/sh
312 printf "Are you sure you want to uninstall $ucproduct? (y/n) : "
313 read answer
314 printf "\\n"
315 if [ "\\\$answer" = "y" ]; then
316         echo "Removing $ucproduct package .."
317         dpkg --remove $product
318         echo "Done!"
319 fi
320 EOFF
321 chmod +x /etc/$baseproduct/uninstall.sh
322 port=`grep "^port=" /etc/$baseproduct/miniserv.conf | sed -e 's/port=//g'`
323 perl -e 'use Net::SSLeay' >/dev/null 2>/dev/null
324 sslmode=0
325 if [ "\$?" = "0" ]; then
326         grep ssl=1 /etc/$baseproduct/miniserv.conf >/dev/null 2>/dev/null
327         if [ "\$?" = "0" ]; then
328                 sslmode=1
329         fi
330 fi
331 if [ "\$sslmode" = "1" ]; then
332         echo "$ucproduct install complete. You can now login to https://\$host:\$port/"
333 else
334         echo "$ucproduct install complete. You can now login to http://\$host:\$port/"
335 fi
336 if [ "$product" = "webmin" ]; then
337         echo "as root with your root password, or as any user who can use sudo"
338         echo "to run commands as root."
339 else
340         echo "as any user on the system."
341 fi
342 EOF
343 close(SCRIPT);
344 system("chmod 755 $postinstall_file");
345
346 # Create the pre-uninstall script
347 open(SCRIPT, ">$preuninstall_file");
348 print SCRIPT <<EOF;
349 #!/bin/sh
350 if [ "\$1" != "upgrade" ]; then
351         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
352         if [ "\$?" = 0 ]; then
353                 # Package is being removed, and no new version of webmin
354                 # has taken it's place. Run uninstalls and stop the server
355                 if [ "$product" = "webmin" ]; then
356                         echo "Running uninstall scripts .."
357                         (cd /usr/share/$baseproduct ; WEBMIN_CONFIG=/etc/$baseproduct WEBMIN_VAR=/var/$baseproduct LANG= /usr/share/$baseproduct/run-uninstalls.pl)
358                 fi
359                 /etc/init.d/$baseproduct stop >/dev/null 2>&1 </dev/null
360                 /etc/$baseproduct/stop >/dev/null 2>&1 </dev/null
361                 /bin/true
362         fi
363 fi
364 EOF
365 close(SCRIPT);
366 system("chmod 755 $preuninstall_file");
367
368 # Create the post-uninstall script
369 open(SCRIPT, ">$postuninstall_file");
370 print SCRIPT <<EOF;
371 #!/bin/sh
372 if [ "\$1" != "upgrade" ]; then
373         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
374         if [ "\$?" = 0 ]; then
375                 # Package is being removed, and no new version of webmin
376                 # has taken it's place. Delete the config files
377                 rm -rf /etc/$baseproduct /var/$baseproduct
378         fi
379 fi
380 EOF
381 close(SCRIPT);
382 system("chmod 755 $postuninstall_file");
383
384 # Run the actual build command
385 system("fakeroot dpkg --build $tmp_dir deb/${product}_${ver}${rel}_all.deb") &&
386         die "dpkg failed";
387 #system("rm -rf $tmp_dir");
388 print "Wrote deb/${product}_${ver}${rel}_all.deb\n";
389 $md5 = `md5sum tarballs/$product-$ver$rel.tar.gz`;
390 $md5 =~ s/\s+.*\n//g;
391 @st = stat("tarballs/$product-$ver.tar.gz");
392
393 # Create the .diff file, which just contains the debian directory
394 $diff_orig_dir = "$tmp_dir/$product-$ver-orig";
395 $diff_new_dir = "$tmp_dir/$product-$ver";
396 mkdir($diff_orig_dir, 0755);
397 mkdir($diff_new_dir, 0755);
398 system("cp -r $debian_dir $diff_new_dir");
399 system("cd $tmp_dir && diff -r -N -u $product-$ver-orig $product-$ver >$webmin_dir/deb/${product}_${ver}${rel}.diff");
400 $diffmd5 = `md5sum deb/${product}_${ver}${rel}.diff`;
401 $diffmd5 =~ s/\s+.*\n//g;
402 @diffst = stat("deb/${product}_${ver}${rel}.diff");
403
404 # Create the .dsc file
405 open(DSC, ">deb/${product}_$ver$rel.plain");
406 print DSC <<EOF;
407 Format: 1.0
408 Source: $product
409 Version: $ver$rel
410 Binary: $product
411 Maintainer: Jamie Cameron <jcameron\@webmin.com>
412 Architecture: all
413 Standards-Version: 3.6.1
414 Build-Depends-Indep: debhelper (>= 4.1.16), debconf (>= 0.5.00), perl
415 Uploaders: Jamie Cameron <jcameron\@webmin.com>
416 Files:
417  $md5 $st[7] ${product}-${ver}.tar.gz
418  $diffmd5 $diffst[7] ${product}_${ver}.diff
419
420 EOF
421 close(DSC);
422 unlink("deb/${product}_$ver$rel.dsc");
423 system("gpg --output deb/${product}_$ver$rel.dsc --clearsign deb/${product}_$ver$rel.plain");
424 unlink("deb/${product}_$ver$rel.plain");
425 print "Wrote source deb/${product}_$ver$rel.dsc\n";
426
427 $dir = "sarge";
428 if (-d "/usr/local/webadmin/deb/repository") {
429         # Add to our repository
430         chdir("/usr/local/webadmin/deb/repository");
431         system("reprepro -Vb . remove $dir $product");
432         system("reprepro -Vb . includedeb $dir ../${product}_${ver}${rel}_all.deb");
433         chdir("/usr/local/webadmin");
434         }
435
436 # Create PGP signature
437 unlink("sigs/${product}_${ver}${rel}_all.deb-sig.asc");
438 system("gpg --armor --output sigs/${product}_${ver}${rel}_all.deb-sig.asc --default-key jcameron\@webmin.com --detach-sig deb/${product}_${ver}${rel}_all.deb");
439
440 # read_file(file, &assoc, [&order], [lowercase])
441 # Fill an associative array with name=value pairs from a file
442 sub read_file
443 {
444 local $_;
445 open(ARFILE, $_[0]) || return 0;
446 while(<ARFILE>) {
447         chomp;
448         local $hash = index($_, "#");
449         local $eq = index($_, "=");
450         if ($hash != 0 && $eq >= 0) {
451                 local $n = substr($_, 0, $eq);
452                 local $v = substr($_, $eq+1);
453                 $_[1]->{$_[3] ? lc($n) : $n} = $v;
454                 push(@{$_[2]}, $n) if ($_[2]);
455                 }
456         }
457 close(ARFILE);
458 if (defined($main::read_file_cache{$_[0]})) {
459         %{$main::read_file_cache{$_[0]}} = %{$_[1]};
460         }
461 return 1;
462 }
463
464 # wrap_lines(text, width)
465 # Given a multi-line string, return an array of lines wrapped to
466 # the given width
467 sub wrap_lines
468 {
469 local @rv;
470 local $w = $_[1];
471 local $rest;
472 foreach $rest (split(/\n/, $_[0])) {
473         if ($rest =~ /\S/) {
474                 while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) {
475                         push(@rv, $1);
476                         $rest = $2;
477                         }
478                 }
479         else {
480                 # Empty line .. keep as it is
481                 push(@rv, $rest);
482                 }
483         }
484 return @rv;
485 }
486
487