All release to be specified in debian package
[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         }
81
82 # Create init script
83 system("mv $usr_dir/$baseproduct-init $init_dir/$baseproduct");
84 foreach $d (@rc_dirs) {
85         system("ln -s ../init.d/$baseproduct $d/S99$baseproduct");
86         }
87 system("echo deb >$usr_dir/install-type");
88 system("echo $product >$usr_dir/deb-name");
89 system("cd $usr_dir && chmod -R og-w .");
90 if ($< == 0) {
91         system("cd $usr_dir && chown -R root:bin .");
92         }
93 $size = int(`du -sk $tmp_dir`);
94
95 # Create the control file
96 open(CONTROL, ">$control_file");
97 print CONTROL <<EOF;
98 Package: $product
99 Version: $ver$rel
100 Section: admin
101 Priority: optional
102 Architecture: all
103 Essential: no
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: A web-based administration interface for Unix systems.
114              Using Webmin you can configure DNS, Samba, NFS, local/remote
115              filesystems and more using your web browser.  After installation,
116              enter the URL https://localhost:10000/ into your browser and
117              login as root with your root 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: A web-based user account administration interface for Unix systems.
124              After installation, enter the URL http://localhost:20000/ into your             browser and login as any user on your system.
125 EOF
126         }
127 close(CONTROL);
128
129 # Create the copyright file
130 $nowstr = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));
131 open(COPY, ">$copyright_file");
132 print COPY <<EOF;
133 This package was debianized by Jamie Cameron <jcameron\@webmin.com> on
134 $nowstr.
135
136 It was downloaded from: http://www.webmin.com/
137
138 Upstream author: Jamie Cameron <jcameron\@webmin.com>
139
140 Copyright:
141
142 EOF
143 open(BSD, "$usr_dir/LICENCE");
144 while(<BSD>) {
145         print COPY $_;
146         }
147 close(BSD);
148 close(COPY);
149
150 # Create the config files file, for those we don't want to replace
151 open(CONF, ">$conffiles_file");
152 print CONF "/etc/pam.d/$baseproduct\n";
153 close(CONF);
154
155 # Get the changes for each module and version
156 $changes = { };
157 foreach $f (sort { $a cmp $b } ( glob("*/CHANGELOG"), "CHANGELOG" )) {
158         # Get the module name and type
159         local $mod = $f =~ /^(\S+)\/CHANGELOG/ ? $1 : "core";
160         next if ($mod ne "core" && -l $mod);
161         local $desc;
162         if ($mod eq "core") {
163                 $desc = "$ucproduct Core";
164                 }
165         else {
166                 local $m = $f;
167                 local %minfo;
168                 $m =~ s/CHANGELOG/module.info/;
169                 &read_file($m, \%minfo);
170                 next if (!$minfo{'longdesc'});
171                 $desc = $minfo{'desc'};
172                 }
173
174         # Read its change log file
175         local $inversion;
176         open(LOG, $f);
177         while(<LOG>) {
178                 s/\r|\n//g;
179                 if (/^----\s+Changes\s+since\s+(\S+)\s+----/) {
180                         $inversion = $1;
181                         }
182                 elsif ($inversion && /\S/) {
183                         push(@{$changes->{$inversion}->{$desc}}, $_);
184                         }
185                 }
186         }
187
188 # Create the changelog file from actual changes, plus the historical changelog
189 open(CHANGELOG, ">$changelog_file");
190 foreach $v (sort { $a <=> $b } (keys %$changes)) {
191         if ($ver > $v && sprintf("%.2f0", $ver) == $v) {
192                 $forv = $ver;
193                 }
194         else {
195                 $forv = sprintf("%.2f0", $v+0.01);
196                 }
197         @st = stat("tarballs/webmin-$forv.tar.gz");
198         $vtimestr = strftime("%a, %d %b %Y %H:%M:%S %z", localtime($st[9]));
199         print CHANGELOG "$baseproduct ($forv) stable; urgency=low\n";
200         print CHANGELOG "\n";
201         foreach $desc (keys %{$changes->{$v}}) {
202                 foreach $c (@{$changes->{$v}->{$desc}}) {
203                         @lines = &wrap_lines("$desc : $c", 65);
204                         print CHANGELOG " * $lines[0]\n";
205                         foreach $l (@lines[1 .. $#lines]) {
206                                 print CHANGELOG "   $l\n";
207                                 }
208                         }
209                 }
210         print CHANGELOG "\n";
211         print CHANGELOG "-- Jamie Cameron <jcameron\@webmin.com> $vtimestr\n";
212         print CHANGELOG "\n";
213         }
214 close(CHANGELOG);
215
216 # Get the temp-directory creator script
217 open(TEMP, "maketemp.pl");
218 while(<TEMP>) {
219         $maketemp .= $_;
220         }
221 close(TEMP);
222 $maketemp =~ s/\\/\\\\/g;
223 $maketemp =~ s/`/\\`/g;
224 $maketemp =~ s/\$/\\\$/g;
225
226 # Create the pre-install script
227 # No need for an OS check, as all debians are supported.
228 open(SCRIPT, ">$preinstall_file");
229 print SCRIPT <<EOF;
230 #!/bin/sh
231 perl <<EOD;
232 $maketemp
233 EOD
234 if [ "\$1" != "upgrade" ]; then
235         if [ "\$WEBMIN_PORT\" != \"\" ]; then
236                 port=\$WEBMIN_PORT
237         else
238                 port=$port
239         fi
240         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
241         if [ "\$?" != "0" ]; then
242                 echo Port \$port is already in use
243                 exit 2
244         fi
245 fi
246 EOF
247 close(SCRIPT);
248 system("chmod 755 $preinstall_file");
249
250 # Create the post-install script
251 open(SCRIPT, ">$postinstall_file");
252 print SCRIPT <<EOF;
253 #!/bin/sh
254 inetd=`grep "^inetd=" /etc/$baseproduct/miniserv.conf 2>/dev/null | sed -e 's/inetd=//g'`
255 if [ "\$1" = "upgrade" ]; then
256         # Upgrading the package, so stop the old webmin properly
257         if [ "\$inetd" != "1" ]; then
258                 /etc/init.d/$baseproduct stop >/dev/null 2>&1 </dev/null
259         fi
260 fi
261 cd /usr/share/$baseproduct
262 config_dir=/etc/$baseproduct
263 var_dir=/var/$baseproduct
264 perl=/usr/bin/perl
265 autoos=3
266 if [ "\$WEBMIN_PORT\" != \"\" ]; then
267         port=\$WEBMIN_PORT
268 else
269         port=$port
270 fi
271 login=root
272 if [ -r /etc/shadow ]; then
273         crypt=x
274 else
275         crypt=`grep "^root:" /etc/passwd | cut -f 2 -d :`
276 fi
277 host=`hostname`
278 ssl=1
279 atboot=1
280 nochown=1
281 autothird=1
282 noperlpath=1
283 nouninstall=1
284 nostart=1
285 export config_dir var_dir perl autoos port login crypt host ssl nochown autothird noperlpath nouninstall nostart allow atboot
286 tempdir=/tmp/.webmin
287 if [ ! -d \$tempdir ]; then
288         tempdir=/tmp
289 fi
290 ./setup.sh >$tempdir/$product-setup.out 2>&1
291 if [ "$product" = "webmin" ]; then
292         grep sudo= /etc/$product/miniserv.conf >/dev/null 2>&1
293         if [ "\$?" = 1 ]; then
294                 # Allow sudo-based logins for Ubuntu
295                 echo sudo=1 >>/etc/$product/miniserv.conf
296         fi
297 fi
298 rm -f /var/lock/subsys/$baseproduct
299 if [ "$inetd" != "1" ]; then
300         if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
301                 invoke-rc.d $baseproduct start >/dev/null 2>&1 </dev/null
302         else
303                 /etc/init.d/$baseproduct start >/dev/null 2>&1 </dev/null
304         fi
305 fi
306 cat >/etc/$baseproduct/uninstall.sh <<EOFF
307 #!/bin/sh
308 printf "Are you sure you want to uninstall $ucproduct? (y/n) : "
309 read answer
310 printf "\\n"
311 if [ "\\\$answer" = "y" ]; then
312         echo "Removing $ucproduct package .."
313         dpkg --remove $product
314         echo "Done!"
315 fi
316 EOFF
317 chmod +x /etc/$baseproduct/uninstall.sh
318 port=`grep "^port=" /etc/$baseproduct/miniserv.conf | sed -e 's/port=//g'`
319 perl -e 'use Net::SSLeay' >/dev/null 2>/dev/null
320 sslmode=0
321 if [ "\$?" = "0" ]; then
322         grep ssl=1 /etc/$baseproduct/miniserv.conf >/dev/null 2>/dev/null
323         if [ "\$?" = "0" ]; then
324                 sslmode=1
325         fi
326 fi
327 if [ "\$sslmode" = "1" ]; then
328         echo "$ucproduct install complete. You can now login to https://\$host:\$port/"
329 else
330         echo "$ucproduct install complete. You can now login to http://\$host:\$port/"
331 fi
332 if [ "$product" = "webmin" ]; then
333         echo "as root with your root password, or as any user who can use sudo"
334         echo "to run commands as root."
335 else
336         echo "as any user on the system."
337 fi
338 EOF
339 close(SCRIPT);
340 system("chmod 755 $postinstall_file");
341
342 # Create the pre-uninstall script
343 open(SCRIPT, ">$preuninstall_file");
344 print SCRIPT <<EOF;
345 #!/bin/sh
346 if [ "\$1" != "upgrade" ]; then
347         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
348         if [ "\$?" = 0 ]; then
349                 # Package is being removed, and no new version of webmin
350                 # has taken it's place. Run uninstalls and stop the server
351                 if [ "$product" = "webmin" ]; then
352                         echo "Running uninstall scripts .."
353                         (cd /usr/share/$baseproduct ; WEBMIN_CONFIG=/etc/$baseproduct WEBMIN_VAR=/var/$baseproduct LANG= /usr/share/$baseproduct/run-uninstalls.pl)
354                 fi
355                 /etc/init.d/$baseproduct stop >/dev/null 2>&1 </dev/null
356                 /etc/$baseproduct/stop >/dev/null 2>&1 </dev/null
357                 /bin/true
358         fi
359 fi
360 EOF
361 close(SCRIPT);
362 system("chmod 755 $preuninstall_file");
363
364 # Create the post-uninstall script
365 open(SCRIPT, ">$postuninstall_file");
366 print SCRIPT <<EOF;
367 #!/bin/sh
368 if [ "\$1" != "upgrade" ]; then
369         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
370         if [ "\$?" = 0 ]; then
371                 # Package is being removed, and no new version of webmin
372                 # has taken it's place. Delete the config files
373                 rm -rf /etc/$baseproduct /var/$baseproduct
374         fi
375 fi
376 EOF
377 close(SCRIPT);
378 system("chmod 755 $postuninstall_file");
379
380 # Run the actual build command
381 system("fakeroot dpkg --build $tmp_dir deb/${product}_${ver}${rel}_all.deb") &&
382         die "dpkg failed";
383 #system("rm -rf $tmp_dir");
384 print "Wrote deb/${product}_${ver}${rel}_all.deb\n";
385 $md5 = `md5sum tarballs/$product-$ver$rel.tar.gz`;
386 $md5 =~ s/\s+.*\n//g;
387 @st = stat("tarballs/$product-$ver.tar.gz");
388
389 # Create the .diff file, which just contains the debian directory
390 $diff_orig_dir = "$tmp_dir/$product-$ver-orig";
391 $diff_new_dir = "$tmp_dir/$product-$ver";
392 mkdir($diff_orig_dir, 0755);
393 mkdir($diff_new_dir, 0755);
394 system("cp -r $debian_dir $diff_new_dir");
395 system("cd $tmp_dir && diff -r -N -u $product-$ver-orig $product-$ver >$webmin_dir/deb/${product}_${ver}${rel}.diff");
396 $diffmd5 = `md5sum deb/${product}_${ver}${rel}.diff`;
397 $diffmd5 =~ s/\s+.*\n//g;
398 @diffst = stat("deb/${product}_${ver}${rel}.diff");
399
400 # Create the .dsc file
401 open(DSC, ">deb/${product}_$ver$rel.plain");
402 print DSC <<EOF;
403 Format: 1.0
404 Source: $product
405 Version: $ver$rel
406 Binary: $product
407 Maintainer: Jamie Cameron <jcameron\@webmin.com>
408 Architecture: all
409 Standards-Version: 3.6.1
410 Build-Depends-Indep: debhelper (>= 4.1.16), debconf (>= 0.5.00), perl
411 Uploaders: Jamie Cameron <jcameron\@webmin.com>
412 Files:
413  $md5 $st[7] ${product}-${ver}.tar.gz
414  $diffmd5 $diffst[7] ${product}_${ver}.diff
415
416 EOF
417 close(DSC);
418 unlink("deb/${product}_$ver$rel.dsc");
419 system("gpg --output deb/${product}_$ver$rel.dsc --clearsign deb/${product}_$ver$rel.plain");
420 unlink("deb/${product}_$ver$rel.plain");
421 print "Wrote source deb/${product}_$ver$rel.dsc\n";
422
423 if (-d "/usr/local/webadmin/deb/repository") {
424         # Add to our repository
425         chdir("/usr/local/webadmin/deb/repository");
426         system("reprepro -Vb . remove sarge $product");
427         system("reprepro -Vb . includedeb sarge ../${product}_${ver}${rel}_all.deb");
428         chdir("/usr/local/webadmin");
429         }
430
431 # Create PGP signature
432 unlink("sigs/${product}_${ver}${rel}_all.deb-sig.asc");
433 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");
434
435 # read_file(file, &assoc, [&order], [lowercase])
436 # Fill an associative array with name=value pairs from a file
437 sub read_file
438 {
439 local $_;
440 open(ARFILE, $_[0]) || return 0;
441 while(<ARFILE>) {
442         chomp;
443         local $hash = index($_, "#");
444         local $eq = index($_, "=");
445         if ($hash != 0 && $eq >= 0) {
446                 local $n = substr($_, 0, $eq);
447                 local $v = substr($_, $eq+1);
448                 $_[1]->{$_[3] ? lc($n) : $n} = $v;
449                 push(@{$_[2]}, $n) if ($_[2]);
450                 }
451         }
452 close(ARFILE);
453 if (defined($main::read_file_cache{$_[0]})) {
454         %{$main::read_file_cache{$_[0]}} = %{$_[1]};
455         }
456 return 1;
457 }
458
459 # wrap_lines(text, width)
460 # Given a multi-line string, return an array of lines wrapped to
461 # the given width
462 sub wrap_lines
463 {
464 local @rv;
465 local $w = $_[1];
466 local $rest;
467 foreach $rest (split(/\n/, $_[0])) {
468         if ($rest =~ /\S/) {
469                 while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) {
470                         push(@rv, $1);
471                         $rest = $2;
472                         }
473                 }
474         else {
475                 # Empty line .. keep as it is
476                 push(@rv, $rest);
477                 }
478         }
479 return @rv;
480 }
481
482