Use debian-friendly default PAM config
[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 ./setup.sh >/tmp/.webmin/$product-setup.out 2>&1
286 if [ "$product" = "webmin" ]; then
287         grep sudo= /etc/$product/miniserv.conf >/dev/null 2>&1
288         if [ "\$?" = 1 ]; then
289                 # Allow sudo-based logins for Ubuntu
290                 echo sudo=1 >>/etc/$product/miniserv.conf
291         fi
292 fi
293 rm -f /var/lock/subsys/$baseproduct
294 if [ "$inetd" != "1" ]; then
295         if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
296                 invoke-rc.d $baseproduct start >/dev/null 2>&1 </dev/null
297         else
298                 /etc/init.d/$baseproduct start >/dev/null 2>&1 </dev/null
299         fi
300 fi
301 cat >/etc/$baseproduct/uninstall.sh <<EOFF
302 #!/bin/sh
303 printf "Are you sure you want to uninstall $ucproduct? (y/n) : "
304 read answer
305 printf "\\n"
306 if [ "\\\$answer" = "y" ]; then
307         echo "Removing $ucproduct package .."
308         dpkg --remove $product
309         echo "Done!"
310 fi
311 EOFF
312 chmod +x /etc/$baseproduct/uninstall.sh
313 port=`grep "^port=" /etc/$baseproduct/miniserv.conf | sed -e 's/port=//g'`
314 perl -e 'use Net::SSLeay' >/dev/null 2>/dev/null
315 sslmode=0
316 if [ "\$?" = "0" ]; then
317         grep ssl=1 /etc/$baseproduct/miniserv.conf >/dev/null 2>/dev/null
318         if [ "\$?" = "0" ]; then
319                 sslmode=1
320         fi
321 fi
322 if [ "\$sslmode" = "1" ]; then
323         echo "$ucproduct install complete. You can now login to https://\$host:\$port/"
324 else
325         echo "$ucproduct install complete. You can now login to http://\$host:\$port/"
326 fi
327 if [ "$product" = "webmin" ]; then
328         echo "as root with your root password, or as any user who can use sudo"
329         echo "to run commands as root."
330 else
331         echo "as any user on the system."
332 fi
333 EOF
334 close(SCRIPT);
335 system("chmod 755 $postinstall_file");
336
337 # Create the pre-uninstall script
338 open(SCRIPT, ">$preuninstall_file");
339 print SCRIPT <<EOF;
340 #!/bin/sh
341 if [ "\$1" != "upgrade" ]; then
342         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
343         if [ "\$?" = 0 ]; then
344                 # Package is being removed, and no new version of webmin
345                 # has taken it's place. Run uninstalls and stop the server
346                 if [ "$product" = "webmin" ]; then
347                         echo "Running uninstall scripts .."
348                         (cd /usr/share/$baseproduct ; WEBMIN_CONFIG=/etc/$baseproduct WEBMIN_VAR=/var/$baseproduct LANG= /usr/share/$baseproduct/run-uninstalls.pl)
349                 fi
350                 /etc/init.d/$baseproduct stop >/dev/null 2>&1 </dev/null
351                 /etc/$baseproduct/stop >/dev/null 2>&1 </dev/null
352                 /bin/true
353         fi
354 fi
355 EOF
356 close(SCRIPT);
357 system("chmod 755 $preuninstall_file");
358
359 # Create the post-uninstall script
360 open(SCRIPT, ">$postuninstall_file");
361 print SCRIPT <<EOF;
362 #!/bin/sh
363 if [ "\$1" != "upgrade" ]; then
364         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
365         if [ "\$?" = 0 ]; then
366                 # Package is being removed, and no new version of webmin
367                 # has taken it's place. Delete the config files
368                 rm -rf /etc/$baseproduct /var/$baseproduct
369         fi
370 fi
371 EOF
372 close(SCRIPT);
373 system("chmod 755 $postuninstall_file");
374
375 # Run the actual build command
376 system("fakeroot dpkg --build $tmp_dir deb/${product}_${ver}_all.deb") &&
377         die "dpkg failed";
378 #system("rm -rf $tmp_dir");
379 print "Wrote deb/${product}_${ver}_all.deb\n";
380 $md5 = `md5sum tarballs/$product-$ver.tar.gz`;
381 $md5 =~ s/\s+.*\n//g;
382 @st = stat("tarballs/$product-$ver.tar.gz");
383
384 # Create the .diff file, which just contains the debian directory
385 $diff_orig_dir = "$tmp_dir/$product-$ver-orig";
386 $diff_new_dir = "$tmp_dir/$product-$ver";
387 mkdir($diff_orig_dir, 0755);
388 mkdir($diff_new_dir, 0755);
389 system("cp -r $debian_dir $diff_new_dir");
390 system("cd $tmp_dir && diff -r -N -u $product-$ver-orig $product-$ver >$webmin_dir/deb/${product}_${ver}.diff");
391 $diffmd5 = `md5sum deb/${product}_${ver}.diff`;
392 $diffmd5 =~ s/\s+.*\n//g;
393 @diffst = stat("deb/${product}_${ver}.diff");
394
395 # Create the .dsc file
396 open(DSC, ">deb/${product}_$ver.plain");
397 print DSC <<EOF;
398 Format: 1.0
399 Source: $product
400 Version: $ver
401 Binary: $product
402 Maintainer: Jamie Cameron <jcameron\@webmin.com>
403 Architecture: all
404 Standards-Version: 3.6.1
405 Build-Depends-Indep: debhelper (>= 4.1.16), debconf (>= 0.5.00), perl
406 Uploaders: Jamie Cameron <jcameron\@webmin.com>
407 Files:
408  $md5 $st[7] ${product}-${ver}.tar.gz
409  $diffmd5 $diffst[7] ${product}_${ver}.diff
410
411 EOF
412 close(DSC);
413 unlink("deb/${product}_$ver.dsc");
414 system("gpg --output deb/${product}_$ver.dsc --clearsign deb/${product}_$ver.plain");
415 unlink("deb/${product}_$ver.plain");
416 print "Wrote source deb/${product}_$ver.dsc\n";
417
418 if (-d "/usr/local/webadmin/deb/repository") {
419         # Add to our repository
420         chdir("/usr/local/webadmin/deb/repository");
421         system("reprepro -Vb . remove sarge $product");
422         system("reprepro -Vb . includedeb sarge ../${product}_${ver}_all.deb");
423         chdir("/usr/local/webadmin");
424         }
425
426 # Create PGP signature
427 unlink("sigs/${product}_${ver}_all.deb-sig.asc");
428 system("gpg --armor --output sigs/${product}_${ver}_all.deb-sig.asc --default-key jcameron\@webmin.com --detach-sig deb/${product}_${ver}_all.deb");
429
430 # read_file(file, &assoc, [&order], [lowercase])
431 # Fill an associative array with name=value pairs from a file
432 sub read_file
433 {
434 local $_;
435 open(ARFILE, $_[0]) || return 0;
436 while(<ARFILE>) {
437         chomp;
438         local $hash = index($_, "#");
439         local $eq = index($_, "=");
440         if ($hash != 0 && $eq >= 0) {
441                 local $n = substr($_, 0, $eq);
442                 local $v = substr($_, $eq+1);
443                 $_[1]->{$_[3] ? lc($n) : $n} = $v;
444                 push(@{$_[2]}, $n) if ($_[2]);
445                 }
446         }
447 close(ARFILE);
448 if (defined($main::read_file_cache{$_[0]})) {
449         %{$main::read_file_cache{$_[0]}} = %{$_[1]};
450         }
451 return 1;
452 }
453
454 # wrap_lines(text, width)
455 # Given a multi-line string, return an array of lines wrapped to
456 # the given width
457 sub wrap_lines
458 {
459 local @rv;
460 local $w = $_[1];
461 local $rest;
462 foreach $rest (split(/\n/, $_[0])) {
463         if ($rest =~ /\S/) {
464                 while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) {
465                         push(@rv, $1);
466                         $rest = $2;
467                         }
468                 }
469         else {
470                 # Empty line .. keep as it is
471                 push(@rv, $rest);
472                 }
473         }
474 return @rv;
475 }
476
477