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