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