]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
bdbcf6afe8c41df8648a7456cadd0c78580df866
[user/henk/code/inspircd.git] / configure
1 #!/usr/bin/env perl
2
3 #
4 # InspIRCd -- Internet Relay Chat Daemon
5 #
6 #   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
7 #   Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
8 #   Copyright (C) 2003, 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
9 #   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
10 #   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
11 #   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
12 #   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
13 #   Copyright (C) 2003-2006 Craig McLure <craig@chatspike.net>
14 #
15 # This file is part of InspIRCd.  InspIRCd is free software: you can
16 # redistribute it and/or modify it under the terms of the GNU General Public
17 # License as published by the Free Software Foundation, version 2.
18 #
19 # This program is distributed in the hope that it will be useful, but WITHOUT
20 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22 # details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 #
27
28
29 BEGIN {
30         require 5.8.0;
31 }
32
33 use strict;
34 use warnings FATAL => qw(all);
35
36 use File::Copy ();
37 use Socket;
38 use Cwd;
39 use Getopt::Long;
40
41 # Utility functions for our buildsystem
42 use make::utilities;
43 use make::configure;
44 use make::gnutlscert;
45 use make::opensslcert;
46
47 ###############################################################################################
48 #
49 #                                 NON-EDITABLE VARIABLES
50 #
51 ###############################################################################################
52
53 our ($opt_use_gnutls, $opt_rebuild, $opt_use_openssl, $opt_nointeractive, $opt_ports,
54     $opt_epoll, $opt_kqueue, $opt_noports, $opt_noepoll, $opt_nokqueue,
55     $opt_noipv6, $opt_maxbuf, $opt_disable_debug, $opt_freebsd_port,
56         $opt_system, $opt_uid);
57
58 our ($opt_cc, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir, $opt_data_dir, $opt_log_dir);
59
60 sub list_extras ();
61
62 sub enable_extras (@);
63
64 sub disable_extras (@);
65
66 my @opt_enableextras;
67 my @opt_disableextras;
68
69 GetOptions (
70         'enable-gnutls' => \$opt_use_gnutls,
71         'rebuild' => \$opt_rebuild,
72         'system' => \$opt_system,
73         'uid=s' => \$opt_uid,
74         'enable-openssl' => \$opt_use_openssl,
75         'disable-interactive' => \$opt_nointeractive,
76         'enable-ports' => \$opt_ports,
77         'enable-epoll' => \$opt_epoll,
78         'enable-kqueue' => \$opt_kqueue,
79         'disable-ports' => \$opt_noports,
80         'disable-epoll' => \$opt_noepoll,
81         'disable-kqueue' => \$opt_nokqueue,
82         'disable-ipv6' => \$opt_noipv6,
83         'with-cc=s' => \$opt_cc,
84         'with-maxbuf=i' => \$opt_maxbuf,
85         'enable-freebsd-ports-openssl' => \$opt_freebsd_port,
86         'prefix=s' => \$opt_base_dir,
87         'config-dir=s' => \$opt_config_dir,
88         'module-dir=s' => \$opt_module_dir,
89         'binary-dir=s' => \$opt_binary_dir,
90         'data-dir=s' => \$opt_data_dir,
91         'log-dir=s' => \$opt_log_dir,
92         'disable-debuginfo' => sub { $opt_disable_debug = 1 },
93         'help'  => sub { showhelp(); },
94         'update' => sub { update(); },
95         'clean' => sub { clean(); },
96         'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
97         'enable-extras=s@' => \@opt_enableextras, # ^
98         'disable-extras=s@' => \@opt_disableextras, # ^
99         'generate-openssl-cert' => sub { make_openssl_cert(); exit(0); },
100         'generate-gnutls-cert' => sub { make_gnutls_cert(); exit(0); }
101 );
102
103 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
104         @opt_enableextras = split /,/, join(',', @opt_enableextras);
105         @opt_disableextras = split /,/, join(',', @opt_disableextras);
106         enable_extras(@opt_enableextras);
107         disable_extras(@opt_disableextras);
108         list_extras;
109         print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
110         exit 0;
111 }
112
113 our $interactive = !(
114         (defined $opt_base_dir) ||
115         (defined $opt_config_dir) ||
116         (defined $opt_module_dir) ||
117         (defined $opt_base_dir) ||
118         (defined $opt_binary_dir) ||
119         (defined $opt_data_dir) ||
120         (defined $opt_log_dir) ||
121         (defined $opt_nointeractive) ||
122         (defined $opt_cc) ||
123         (defined $opt_noipv6) ||
124         (defined $opt_kqueue) ||
125         (defined $opt_epoll) ||
126         (defined $opt_ports) ||
127         (defined $opt_use_openssl) ||
128         (defined $opt_nokqueue) ||
129         (defined $opt_noepoll) ||
130         (defined $opt_noports) ||
131         (defined $opt_maxbuf) ||
132         (defined $opt_system) ||
133         (defined $opt_uid) ||
134         (defined $opt_use_gnutls) ||
135         (defined $opt_freebsd_port)
136 );
137
138 chomp(our $topdir = getcwd());
139 our $this = resolve_directory($topdir);                                         # PWD, Regardless.
140 our @modlist = ();                                                                      # Declare for Module List..
141 our %config = ();                                                                       # Initiate Configuration Hash..
142 our $cache_loaded = getcache();
143 $config{ME} = resolve_directory($topdir);                               # Present Working Directory
144
145 $config{BASE_DIR} ||= $config{ME}."/run";
146
147 if (defined $opt_base_dir) {
148         $config{BASE_DIR} = $opt_base_dir;
149 } elsif (defined $opt_system) {
150         $config{BASE_DIR} = '/var/lib/inspircd';
151 }
152
153 if (defined $opt_system) {
154         $config{UID} = $opt_uid || 'ircd';
155         $config{CONFIG_DIR}      = '/etc/inspircd';
156         $config{MODULE_DIR}      = '/usr/lib/inspircd';
157         $config{BINARY_DIR}      = '/usr/sbin/';
158         $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
159         $config{DATA_DIR}        = '/var/inspircd';
160         $config{LOG_DIR}         = '/var/log/inspircd';
161 } else {
162         $config{UID} = $opt_uid || $config{UID} || $<;
163         $config{CONFIG_DIR}      ||= resolve_directory($config{BASE_DIR}."/conf");      # Configuration Directory
164         $config{MODULE_DIR}      ||= resolve_directory($config{BASE_DIR}."/modules");   # Modules Directory
165         $config{BINARY_DIR}      ||= resolve_directory($config{BASE_DIR}."/bin");               # Binary Directory
166         $config{BUILD_DIR}       ||= resolve_directory($config{ME}."/build");         # Build Directory
167         $config{DATA_DIR}        ||= resolve_directory($config{BASE_DIR}."/data");      # Data directory
168         $config{LOG_DIR}         ||= resolve_directory($config{BASE_DIR}."/logs");      # Log directory
169 }
170
171 if (defined $opt_config_dir) {
172         $config{CONFIG_DIR} = $opt_config_dir;
173 }
174 if (defined $opt_module_dir) {
175         $config{MODULE_DIR} = $opt_module_dir;
176 }
177 if (defined $opt_binary_dir) {
178         $config{BINARY_DIR} = $opt_binary_dir;
179 }
180 if (defined $opt_data_dir) {
181         $config{DATA_DIR} = $opt_data_dir;
182 }
183 if (defined $opt_log_dir) {
184         $config{LOG_DIR} = $opt_log_dir;
185 }
186 chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null`); # GNUTLS Version.
187
188 if (defined $opt_freebsd_port)
189 {
190         chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
191         chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);
192         $config{USE_FREEBSD_BASE_SSL} = "n";
193 }
194 else
195 {
196         if ($^O eq "freebsd")
197         {
198                 # default: use base ssl
199                 chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`);                      # OpenSSL version, freebsd specific
200                 chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);      # Port version, may be different
201         }
202         else
203         {
204                 chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);           # Openssl version, others
205                 $config{HAS_OPENSSL_PORT} = "";
206                 $config{USE_FREEBSD_BASE_SSL} = "n";
207         }
208 }
209
210 chomp(our $gnutls_ver = $config{HAS_GNUTLS});
211 chomp(our $openssl_ver = $config{HAS_OPENSSL});
212 $config{USE_GNUTLS}         ||= "n";
213 if (defined $opt_use_gnutls)
214 {
215         $config{USE_GNUTLS} = "y";                                      # Use gnutls.
216 }
217 $config{USE_OPENSSL}    ||= "n";                                                # Use openssl.
218 if (defined $opt_use_openssl)
219 {
220         $config{USE_OPENSSL} = "y";
221 }
222
223 if (!defined $opt_disable_debug) {
224         $config{OPTIMISATI}      = "-g1";                               # Optimisation Flag
225 } else {
226         $config{OPTIMISATI}      = "-O2";
227 }
228
229 $config{HAS_STRLCPY}    = "false";                                      # strlcpy Check.
230 $config{HAS_STDINT}      = "false";                                     # stdint.h check
231 $config{USE_KQUEUE}      = "y";                                         # kqueue enabled
232 if (defined $opt_nokqueue) {
233         $config{USE_KQUEUE} = "n";
234 }
235 $config{USE_POLL}     = "y";                                    # poll enabled
236 $config{USE_EPOLL}        = "y";                                        # epoll enabled
237 if (defined $opt_noepoll)
238 {
239         $config{USE_EPOLL} = "n";
240 }
241 $config{USE_PORTS}        = "y";                                        # epoll enabled
242 if (defined $opt_noports)
243 {
244         $config{USE_PORTS} = "n";
245 }
246 $config{_SOMAXCONN} = SOMAXCONN;                                        # Max connections in accept queue
247 $config{OSNAME}             = $^O;                                      # Operating System Name
248 $config{IS_DARWIN}        = "NO";                                       # Is OSX?
249 $config{STARTSCRIPT}      = "inspircd";                 # start script?
250 $config{DESTINATION}      = "BASE";                             # Is target path.
251 if ($config{OSNAME} =~ /darwin/i)
252 {
253         $config{IS_DARWIN} = "YES";
254         $config{STARTSCRIPT}      = "org.inspircd.plist";               # start script for OSX.
255         $config{CC}                 = "xcrun clang++";                                  # C++ compiler for OSX.
256 }
257 elsif ($config{OSNAME} =~ /freebsd/i)
258 {
259         chomp(my $fbsd_version = `uname -r`);
260         $fbsd_version =~ s/^(\d+\.\d+).*/$1/g;
261         $config{CC} = $fbsd_version >= 10.0 ? 'clang++' : 'g++';
262 }
263 else
264 {
265         $config{CC}                 = "g++";                                            # C++ compiler
266 }
267 if (defined $opt_cc)
268 {
269         $config{CC} = $opt_cc;
270 }
271 our $exec = $config{CC} . " -dumpversion | cut -c 1";
272 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
273 $exec = $config{CC} . " -dumpversion | cut -c 3";
274 chomp($config{GCCMINOR}         = `$exec`);
275 $config{MAXBUF}                 = "512";                                # Max buffer size
276
277 if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
278         $config{HAS_OPENSSL} = $1;
279 } else {
280         $config{HAS_OPENSSL} = "";
281 }
282
283 if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
284         print "`$config{CC}` was not found! A C++ compiler is required to build InspIRCd!\n";
285         print "You can pass a custom compiler to $0 using --with-cc=[name].\n";
286         exit;
287 }
288
289 # Get and Set some important vars..
290 getmodules();
291
292 sub clean
293 {
294         unlink(".config.cache");
295 }
296
297 our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
298
299 sub update
300 {
301         eval {
302                 chomp($topdir = getcwd());
303                 $this = resolve_directory($topdir);                                          # PWD, Regardless.
304                 getmodules();
305                 # Does the cache file exist?
306                 if (!getcache()) {
307                         # No, No it doesn't.. *BASH*
308                         print "You have not run ./configure before. Please do this before trying to run the update script.\n";
309                         exit 0;
310                 } else {
311                         # We've Loaded the cache file and all our variables..
312                         print "Updating files...\n";
313                         if (defined($opt_disable_debug) && $opt_disable_debug == 1)
314                         {
315                                 print "Disabling debug information (-g).\n";
316                                 $config{OPTIMISATI} = "";
317                         }
318                         $has_epoll = $config{HAS_EPOLL};
319                         $has_ports = $config{HAS_PORTS};
320                         $has_kqueue = $config{HAS_KQUEUE};
321                         writefiles(1);
322                         makecache();
323                         print "Complete.\n";
324                         exit;
325                 }
326         };
327         if ($@)
328         {
329                 print "Configure update failed: $@\n";
330         }
331         exit;
332 }
333
334
335 sub test_compile {
336         my $feature = shift;
337         my $fail = 0;
338         $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1";
339         $fail ||= system "./test_$feature";
340         unlink "test_$feature";
341         return !$fail;
342 }
343
344 print "Running non-interactive configure...\n" unless $interactive;
345 print "Checking for cache from previous configure... ";
346 print ($cache_loaded ? "found\n" : "not found\n");
347 $config{SYSTEM} = lc $^O;
348 print "Checking operating system version... $config{SYSTEM}\n";
349
350 $exec = $config{CC} . " -dumpversion | cut -c 1";
351 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
352 $exec = $config{CC} . " -dumpversion | cut -c 3";
353 chomp($config{GCCMINOR}         = `$exec`);
354
355 printf "Checking if stdint.h exists... ";
356 $config{HAS_STDINT} = "true";
357 our $fail = 0;
358 open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
359 if ($config{HAS_STDINT} eq "true") {
360         close(STDINT);
361 }
362 print "yes\n" if $config{HAS_STDINT} eq "true";
363 print "no\n" if $config{HAS_STDINT} eq "false";
364
365 printf "Checking if strlcpy exists... ";
366 # Perform the strlcpy() test..
367 $config{HAS_STRLCPY} = "false";
368 $fail = 0;
369 open(STRLCPY, "</usr/include/string.h") or $fail = 1;
370 if (!$fail) {
371         while (defined(my $line = <STRLCPY>)) {
372                 chomp($line);
373                 # try and find the delcaration of:
374                 # size_t strlcpy(...)
375                 if ($line =~ /size_t(\0x9|\s)+strlcpy/) {
376                         $config{HAS_STRLCPY} = "true";
377                 }
378         }
379         close(STRLCPY);
380 }
381 print "yes\n" if $config{HAS_STRLCPY} eq "true";
382 print "no\n" if $config{HAS_STRLCPY} eq "false";
383
384 printf "Checking if kqueue exists... ";
385 $has_kqueue = test_compile('kqueue');
386 print $has_kqueue ? "yes\n" : "no\n";
387
388 printf "Checking for epoll support... ";
389 $has_epoll = test_compile('epoll');
390 print $has_epoll ? "yes\n" : "no\n";
391
392 printf "Checking for eventfd support... ";
393 $config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false';
394 print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n";
395
396 printf "Checking if Solaris I/O completion ports are available... ";
397 $has_ports = 0;
398 our $system = `uname -s`;
399 chomp ($system);
400 $has_ports = 1 if ($system eq "SunOS");
401
402 if ($has_ports) {
403         my $kernel = `uname -r`;
404         chomp($kernel);
405         if (($kernel !~ /^5\.1./)) {
406                 $has_ports = 0;
407         }
408 }
409 print "yes\n" if $has_ports == 1;
410 print "no\n" if $has_ports == 0;
411
412 $config{HAS_EPOLL} = $has_epoll;
413 $config{HAS_KQUEUE} = $has_kqueue;
414
415 printf "Checking for libgnutls... ";
416 if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
417         if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
418                 print "yes\n";
419                 $config{HAS_GNUTLS} = "y";
420         } else {
421                 print "no\n";
422                 $config{HAS_GNUTLS} = "n";
423         }
424 } else {
425         print "no\n";
426         $config{HAS_GNUTLS} = "n";
427 }
428
429 printf "Checking for openssl... ";
430 if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
431         if (defined($openssl_ver) && ($openssl_ver ne "")) {
432                 print "yes\n";
433                 $config{HAS_OPENSSL} = "y";
434         } else {
435                 print "no\n";
436                 $config{HAS_OPENSSL} = "n";
437         }
438 } else {
439         print "no\n";
440         $config{HAS_OPENSSL} = "n";
441 }
442
443 printf "Checking if you are running an ancient, unsupported OS... ";
444 if ($config{OSNAME} =~ /FreeBSD/i)
445 {
446         my $version = `uname -r`;
447         if ($version =~ /^4\./)
448         {
449                 print "yes.\n";
450                 print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
451                 print "To build, you will need to add the following to CXXFLAGS:\n";
452                 print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
453         }
454         else
455         {
456                 print "no ($version)\n";
457         }
458 }
459 else
460 {
461         print "no ($config{OSNAME})\n";
462 }
463
464 ################################################################################
465 #                         BEGIN INTERACTIVE PART                              #
466 ################################################################################
467
468 # Clear the Screen..
469 if ($interactive)
470 {
471         print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
472         my $wholeos = $^O;
473
474         my $rev = getrevision();
475         # Display Introduction Message..
476         print <<"STOP" ;
477 Welcome to the \e[1mInspIRCd\e[0m configuration program! (\e[1minteractive mode\e[0m)
478 \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
479
480 *** If you are unsure of any of these values, leave it blank for    ***
481 *** standard settings that will work, and your server will run      ***
482 *** using them. Please consult your IRC network admin if in doubt.  ***
483
484 Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
485 a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
486 dir, otherwise you won't have a config file!
487
488 Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
489 Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
490 STOP
491         if ($rev eq "r0") {
492                 print " (Non-SVN build)";
493         }
494         print ".\n\n";
495
496         $config{CHANGE_COMPILER} = "n";
497         print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
498
499         while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
500                 print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
501 should NOT be used. You should probably specify a newer compiler.\n\n";
502                 yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
503                 if ($config{CHANGE_COMPILER} =~ /y/i) {
504                         print "What command do you want to use to invoke your compiler?\n";
505                         print "[\e[1;32m$config{CC}\e[0m] -> ";
506                         chomp($config{CC} = <STDIN>);
507                         if ($config{CC} eq "") {
508                                 $config{CC} = "g++";
509                         }
510                         chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
511                         if ($foo ne "") {
512                                 chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
513                                 chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
514                                 print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
515                                 if ($config{GCCVER} < 3) {
516                                         print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
517                                 }
518                         }
519                         else {
520                                 print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
521                         }
522                 }
523         }
524
525         print "\n";
526
527         # Directory Settings..
528         my $tmpbase = $config{BASE_DIR};
529         dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
530         if ($tmpbase ne $config{BASE_DIR}) {
531                 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
532                 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
533                 $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data Directory
534                 $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log Directory
535                 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
536         }
537
538         dir_check("are the configuration files", "CONFIG_DIR");
539         dir_check("are the modules to be compiled to", "MODULE_DIR");
540         dir_check("is the IRCd binary to be placed", "BINARY_DIR");
541         dir_check("are variable data files to be located in", "DATA_DIR");
542         dir_check("are the logs to be stored in", "LOG_DIR");
543         dir_check("do you want the build to take place", "BUILD_DIR");
544                 
545         my $chose_hiperf = 0;
546         if ($has_kqueue) {
547                 yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?");
548                 print "\n";
549                 if ($config{USE_KQUEUE} eq "y") {
550                         $chose_hiperf = 1;
551                 }
552         }
553         if ($has_epoll) {
554                 yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?");
555                 print "\n";
556                 if ($config{USE_EPOLL} eq "y") {
557                         $chose_hiperf = 1;
558                 }
559         }
560         if ($has_ports) {
561                 yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?");
562                 print "\n";
563                 if ($config{USE_PORTS} eq "y") {
564                         $chose_hiperf = 1;
565                 }
566         }
567
568         if (!$chose_hiperf) {
569                 yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?");
570                 if ($config{USE_POLL} ne "y")
571                 {
572                         print "No high-performance socket engines are available, or you chose\n";
573                         print "not to enable one. Defaulting to select() engine.\n\n";
574                 }
575         }
576
577         $config{USE_FREEBSD_BASE_SSL} = "n";
578         $config{USE_FREEBSD_PORTS_SSL} = "n";
579         if ($config{HAS_OPENSSL_PORT} ne "")
580         {
581                 $config{USE_FREEBSD_PORTS_SSL} = "y";
582                 print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
583                 print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n";
584                 yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
585                 print "\n";
586                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
587
588                 if ($config{USE_FREEBSD_BASE_SSL} eq "n")
589                 {
590                         # update to port version
591                         $openssl_ver = $config{HAS_OPENSSL_PORT};
592                 }
593         }
594         else
595         {
596                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
597         }
598
599         $config{USE_SSL} ||= "n";
600         $config{MODUPDATE} ||= 'n';
601
602         if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
603         {
604                 print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
605                 print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
606
607                 yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
608                 if ($config{USE_SSL} eq "y")
609                 {
610                         if ($config{HAS_GNUTLS} eq "y")
611                         {
612                                 yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
613                                 if ($config{USE_GNUTLS} eq "y")
614                                 {
615                                         print "\nUsing GnuTLS SSL module.\n";
616                                 }
617                         }
618
619                         if ($config{HAS_OPENSSL} eq "y")
620                         {
621                                 yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
622                                 if ($config{USE_OPENSSL} eq "y")
623                                 {
624                                         print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
625                                 }
626                         }
627                 }
628         }
629         else
630         {
631                 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
632                 print "is in your path.\n\n";
633         }
634
635         yesno('MODUPDATE',"Would you like to check for updates to third-party modules?");
636         print "\n";
637         if ($config{MODUPDATE} eq "y") {
638                 print "Checking for upgrades to extra and third-party modules... ";
639                 system "./modulemanager upgrade";
640         }
641 }
642
643 # We are on a POSIX system, we can enable POSIX extras without asking
644 symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
645
646 dumphash();
647
648 if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
649 {
650         print "Sorry, but I couldn't detect GnuTLS. Make sure pkg-config is in your path.\n";
651         exit(0);
652 }
653 if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
654 {
655         print "Sorry, but I couldn't detect OpenSSL. Make sure pkg-config and openssl are in your path.\n";
656         exit(0);
657 }
658 our $failed = 0;
659
660 $config{CERTGEN} ||= 'y';
661 yesno('CERTGEN',"Would you like to generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
662
663 if ($config{USE_GNUTLS} eq "y") {
664         unless (-r "src/modules/m_ssl_gnutls.cpp") {
665                 print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
666                 symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
667         }
668         if ($interactive && $config{CERTGEN} eq 'y')
669         {
670                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
671                         print "SSL certificates not found, generating.. \n\n
672 *************************************************************
673 * Generating the private key may take some time, once done, *
674 * answer the questions which follow. If you are unsure,     *
675 * just hit enter!                                           *
676 *************************************************************\n\n";
677                         $failed = make_gnutls_cert();
678                         if ($failed) {
679                                 print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
680                         } else {
681                                 print "\nCertificate generation complete, copying to config directory... ";
682                                 File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
683                                 File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
684                                 print "Done.\n\n";
685                         }
686                 }
687                 else {
688                         print "SSL certificates found, skipping.\n\n";
689                 }
690         }
691         else
692         {
693                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
694         }
695 }
696
697 if ($config{USE_OPENSSL} eq "y") {
698         unless (-r "src/modules/m_ssl_openssl.cpp") {
699                 print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
700                 symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
701         }
702         $failed = 0;
703         if ($interactive && $config{CERTGEN} eq 'y')
704         {
705                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
706                         print "SSL certificates not found, generating.. \n\n
707 *************************************************************
708 * Generating the certificates may take some time, go grab a *
709 * coffee or something.                                      *
710 *************************************************************\n\n";
711                         make_openssl_cert();
712                         print "\nCertificate generation complete, copying to config directory... ";
713                         File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
714                         File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
715                         File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
716                         print "Done.\n\n";
717                 } else {
718                         print "SSL certificates found, skipping.\n\n"
719                 }
720         }
721         else
722         {
723                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
724         }
725 }
726 if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
727         print "Skipping SSL certificate generation as SSL support is not available.\n\n";
728 }
729
730 depcheck();
731 writefiles(1);
732 makecache();
733
734 print "\n\n";
735 print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
736 if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
737         print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
738         print "modules in your config. This configure script has added those modules to the\n";
739         print "build process. For more info, please refer to:\n";
740         print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
741 }
742 print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
743 if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
744         print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but I'm letting you\ngo ahead with it anyway, just in case I'm wrong :-)\n";
745 }
746
747 if ($config{GCCVER} < "3") {
748         print <<FOO2;
749 \e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
750 GCC 2.x series compilers only had partial (read as broken) C++ support, and
751 your compile will most likely fail horribly! If you have any problems, do NOT
752 report them to the bugtracker or forums without first upgrading your compiler
753 to a newer 3.x or 4.x (or whatever is available currently) version.
754 FOO2
755 }
756
757 ################################################################################
758 #                             HELPER FUNCTIONS                          #
759 ################################################################################
760 sub getcache {
761         # Retrieves the .config.cache file, and loads values into the main config hash.
762         open(CACHE, ".config.cache") or return 0;
763         while (<CACHE>) {
764                 chomp;
765                 # Ignore Blank lines, and comments..
766                 next if /^\s*$/;
767                 next if /^\s*#/;
768                 my ($key, $value) = split("=", $_, 2);
769                 $value =~ /^\"(.*)\"$/;
770                 # Do something with data here!
771                 $config{$key} = $1;
772         }
773         close(CACHE);
774         return 1;
775 }
776
777 sub makecache {
778         # Dump the contents of %config
779         print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
780         open(FILEHANDLE, ">.config.cache");
781         foreach my $key (keys %config) {
782                 print FILEHANDLE "$key=\"$config{$key}\"\n";
783         }
784         close(FILEHANDLE);
785 }
786
787 sub dir_check {
788         my ($desc, $hash_key) = @_;
789         my $complete = 0;
790         while (!$complete) {
791                 print "In what directory $desc?\n";
792                 print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
793                 chomp(my $var = <STDIN>);
794                 if ($var eq "") {
795                         $var = $config{$hash_key};
796                 }
797                 if ($var =~ /^\~\/(.+)$/) {
798                         # Convert it to a full path..
799                         $var = resolve_directory($ENV{HOME} . "/" . $1);
800                 }
801                 elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
802                 {
803                         # Assume relative Path was given.. fill in the rest.
804                         $var = $this . "/$var";
805                 }
806
807                 $var = resolve_directory($var);
808                 if (! -e $var) {
809                         print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
810                         chomp(my $tmp = <STDIN>);
811                         if (($tmp eq "") || ($tmp =~ /^y/i)) {
812                                 # Attempt to Create the Dir..
813                                 my $chk = eval {
814                                         use File::Path ();
815                                         File::Path::mkpath($var, 0, 0777);
816                                         1;
817                                 };
818                                 unless (defined($chk) && -d $var) {
819                                         print "Unable to create directory. ($var)\n\n";
820                                         # Restart Loop..
821                                         next;
822                                 }
823                         } else {
824                                 # They said they don't want to create, and we can't install there.
825                                 print "\n\n";
826                                 next;
827                         }
828                 } else {
829                         if (!is_dir($var)) {
830                                 # Target exists, but is not a directory.
831                                 print "File $var exists, but is not a directory.\n\n";
832                                 next;
833                         }
834                 }
835                 # Either Dir Exists, or was created fine.
836                 $config{$hash_key} = $var;
837                 $complete = 1;
838                 print "\n";
839         }
840 }
841
842 our $SHARED = "";
843
844 my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
845
846 sub writefiles {
847         my($writeheader) = @_;
848         # First File.. inspircd_config.h
849         chomp(my $incos = `uname -n -s -r`);
850         chomp(my $version = `sh src/version.sh`);
851         chomp(my $revision2 = getrevision());
852         my $branch = "InspIRCd-0.0";
853         if ($version =~ /^(InspIRCd-[0-9]+\.[0-9]+)\.[0-9]+/)
854         {
855                 $branch = $1;
856         }
857         if ($writeheader == 1)
858         {
859                 print "Writing \e[1;32minspircd_config.h\e[0m\n";
860                 open(FILEHANDLE, ">include/inspircd_config.h.tmp");
861                 print FILEHANDLE <<EOF;
862 /* Auto generated by configure, do not modify! */
863 #ifndef __CONFIGURATION_AUTO__
864 #define __CONFIGURATION_AUTO__
865
866 /* this is for windows support. */
867 #define CoreExport /**/
868 #define DllExport /**/
869
870 #define CONFIG_PATH "$config{CONFIG_DIR}"
871 #define DATA_PATH "$config{DATA_DIR}"
872 #define LOG_PATH "$config{LOG_DIR}"
873 #define MOD_PATH "$config{MODULE_DIR}"
874 #define SOMAXCONN_S "$config{_SOMAXCONN}"
875 #define ENTRYPOINT int main(int argc, char** argv)
876
877 EOF
878 print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
879
880                 if ($config{OSNAME} =~ /SunOS/i) {
881                         print FILEHANDLE "#define IS_SOLARIS\n";
882                 }
883                 if ($config{OSNAME} =~ /MINGW32/i) {
884                         print FILEHANDLE "#define IS_MINGW\n";
885                 }
886                 if ($config{GCCVER} >= 3) {
887                         print FILEHANDLE "#define GCC3\n";
888                 }
889                 if ($config{HAS_STRLCPY} eq "true") {
890                         print FILEHANDLE "#define HAS_STRLCPY\n";
891                 }
892                 if ($config{HAS_STDINT} eq "true") {
893                         print FILEHANDLE "#define HAS_STDINT\n";
894                 }
895                 if ($config{HAS_EVENTFD} eq 'true') {
896                         print FILEHANDLE "#define HAS_EVENTFD\n";
897                 }
898                 if ($config{OSNAME} !~ /DARWIN/i) {
899                         print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
900                 }
901                 my $use_hiperf = 0;
902                 if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
903                         print FILEHANDLE "#define USE_KQUEUE\n";
904                         $config{SOCKETENGINE} = "socketengine_kqueue";
905                         $use_hiperf = 1;
906                 }
907                 if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
908                         print FILEHANDLE "#define USE_EPOLL\n";
909                         $config{SOCKETENGINE} = "socketengine_epoll";
910                         $use_hiperf = 1;
911                 }
912                 if (($has_ports) && ($config{USE_PORTS} eq "y")) {
913                         print FILEHANDLE "#define USE_PORTS\n";
914                         $config{SOCKETENGINE} = "socketengine_ports";
915                         $use_hiperf = 1;
916                 }
917                 # user didn't choose either epoll or select for their OS.
918                 # default them to USE_SELECT (ewwy puke puke)
919                 if (!$use_hiperf) {
920                         print "no hi-perf, " . $config{USE_POLL};
921                         if ($config{USE_POLL} eq "y")
922                         {
923                                 print FILEHANDLE "#define USE_POLL\n";
924                                 $config{SOCKETENGINE} = "socketengine_poll";
925                         }
926                         else
927                         {
928                                 print FILEHANDLE "#define USE_SELECT\n";
929                                 $config{SOCKETENGINE} = "socketengine_select";
930                         }
931                 }
932                 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
933                 close(FILEHANDLE);
934
935                 open(FILEHANDLE, ">include/inspircd_version.h.tmp");
936                 print FILEHANDLE <<EOF;
937 #define BRANCH "$branch"
938 #define VERSION "$version"
939 #define REVISION "$revision2"
940 #define SYSTEM "$incos"
941 EOF
942                 close FILEHANDLE;
943
944                 for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
945                         my $diff = 0;
946                         open my $fh1, $file or $diff = 1;
947                         open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
948                         while (!$diff) {
949                                 my $line1 = <$fh1>;
950                                 my $line2 = <$fh2>;
951                                 if (defined($line1) != defined($line2)) {
952                                         $diff = 1;
953                                 } elsif (!defined $line1) {
954                                         last;
955                                 } else {
956                                         $diff = ($line1 ne $line2);
957                                 }
958                         }
959                         if ($diff) {
960                                 unlink $file;
961                                 rename "$file.tmp", $file;
962                         } else {
963                                 unlink "$file.tmp";
964                         }
965                 }
966         }
967
968         # Write all .in files.
969         my $tmp = "";
970         my $file = "";
971         my $exe = "inspircd";
972
973         # Do this once here, and cache it in the .*.inc files,
974         # rather than attempting to read src/version.sh from
975         # compiled code -- we might not have the source to hand.
976         # Fix for bug#177 by Brain.
977
978         chomp($version = `sh ./src/version.sh`);
979         chomp(my $revision = getrevision());
980         $version = "$version(r$revision)";
981
982         # We can actually parse any file starting with . and ending with .inc,
983         # but right now we only parse .inspircd.inc to form './inspircd'
984         prepare_dynamic_makefile();
985
986         my @dotfiles = qw(main.mk inspircd);
987         push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
988
989         foreach my $file (@dotfiles) {
990                 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
991                 $_ = join '', <FILEHANDLE>;
992                 close(FILEHANDLE);
993
994                 $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
995
996                 for my $var (qw(
997                         CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
998                         STARTSCRIPT DESTINATION SOCKETENGINE LOG_DIR
999                 )) {
1000                         s/\@$var\@/$config{$var}/g;
1001                 }
1002                 s/\@EXECUTABLE\@/$exe/ if defined $exe;
1003                 s/\@VERSION\@/$version/ if defined $version;
1004
1005                 if ($file eq 'main.mk') {
1006                         print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
1007
1008                         my $mk_tmp = $_;
1009                         s/\@IFDEF (\S+)/ifdef $1/g;
1010                         s/\@IFNDEF (\S+)/ifndef $1/g;
1011                         s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
1012                         s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
1013                         s/\@ELSE/else/g;
1014                         s/\@ENDIF/endif/g;
1015                         s/ *\@BSD_ONLY .*\n//g;
1016                         s/\@GNU_ONLY //g;
1017                         s/\@DO_EXPORT (.*)/export $1/g;
1018                         open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
1019                         print MKF $_;
1020                         close MKF;
1021
1022                         print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
1023                         $_ = $mk_tmp;
1024                         s/\@IFDEF (\S+)/.if defined($1)/g;
1025                         s/\@IFNDEF (\S+)/.if !defined($1)/g;
1026                         s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
1027                         s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
1028                         s/\@ELSE/.else/g;
1029                         s/\@ENDIF/.endif/g;
1030                         s/\@BSD_ONLY //g;
1031                         s/ *\@GNU_ONLY .*\n//g;
1032                         $mk_tmp = $_;
1033                         $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
1034                         open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
1035                         print MKF $mk_tmp;
1036                         close MKF;
1037                 } else {
1038                         print "Writing \e[1;32m$file\e[0m ...\n";
1039                         open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
1040                         print FILEHANDLE $_;
1041                         close(FILEHANDLE);
1042                 }
1043         }
1044
1045         chmod 0755, 'inspircd';
1046 }
1047
1048 sub depcheck
1049 {
1050         getmodules();
1051         for my $mod (@modlist) {
1052                 getcompilerflags("src/modules/m_$mod.cpp");
1053                 getlinkerflags("src/modules/m_$mod.cpp");
1054         }
1055 }
1056
1057 sub prepare_dynamic_makefile
1058 {
1059         my $i = 0;
1060
1061         if (!$has_epoll)
1062         {
1063                 $config{USE_EPOLL} = 0;
1064         }
1065         if (!$has_kqueue)
1066         {
1067                 $config{USE_KQUEUE} = 0;
1068         }
1069         if (!$has_ports)
1070         {
1071                 $config{USE_PORTS} = 0;
1072         }
1073 }
1074
1075 # Routine to list out the extra/ modules that have been enabled.
1076 # Note: when getting any filenames out and comparing, it's important to lc it if the
1077 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
1078 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
1079 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
1080 sub list_extras () {
1081         use File::Spec;
1082         # @_ not used
1083         my $srcdir = File::Spec->catdir("src", "modules");
1084         my $abs_srcdir = File::Spec->rel2abs($srcdir);
1085         local $_;
1086         my $dd;
1087         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
1088         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1089         closedir $dd;
1090         undef $dd;
1091         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
1092         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1093         closedir $dd;
1094         undef $dd;
1095         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
1096         my %extras = ();
1097 EXTRA:  for my $extra (@extras) {
1098                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
1099                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1100                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
1101                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
1102                 if (-l $abs_source) {
1103                         # Symlink, is it in the right place?
1104                         my $targ = readlink($abs_source);
1105                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
1106                         if ($abs_targ eq $abs_extra) {
1107                                 $extras{$extra} = "\e[32;1menabled\e[0m";
1108                         } else {
1109                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
1110                         }
1111                 } elsif (-e $abs_source) {
1112                         my ($devext, $inoext) = stat($abs_extra);
1113                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
1114                         if ($lnksrc > 1) {
1115                                 if ($devsrc == $devext && $inosrc == $inoext) {
1116                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1117                                 } else {
1118                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
1119                                 }
1120                         } else {
1121                                 open my $extfd, "<", $abs_extra;
1122                                 open my $srcfd, "<", $abs_source;
1123                                 local $/ = undef;
1124                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
1125                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1126                                 } else {
1127                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
1128                                 }
1129                         }
1130                 } else {
1131                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
1132                 }
1133         }
1134         # Now let's add dependency info
1135         for my $extra (keys(%extras)) {
1136                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
1137                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1138                 my @deps = split / +/, getdependencies($abs_extra);
1139                 for my $dep (@deps) {
1140                         if (exists($extras{$dep})) {
1141                                 my $ref = \$extras{$dep}; # Take reference.
1142                                 if ($$ref !~ m/needed by/) {
1143                                         # First dependency found.
1144                                         if ($$ref =~ m/enabled/) {
1145                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
1146                                         } else {
1147                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
1148                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
1149                                         }
1150                                 } else {
1151                                         if ($$ref =~ m/enabled/) {
1152                                                 $$ref .= ", \e[32;1m$extra\e[0m";
1153                                         } else {
1154                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
1155                                         }
1156                                 }
1157                         }
1158                 }
1159         }
1160         for my $extra (sort {$a cmp $b} keys(%extras)) {
1161                 my $text = $extras{$extra};
1162                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
1163                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
1164                 } else {
1165                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
1166                 }
1167         }
1168         return keys(%extras) if wantarray; # Can be used by manage_extras.
1169 }
1170
1171 sub enable_extras (@) {
1172         my (@extras) = @_;
1173         for my $extra (@extras) {
1174                 my $extrapath = "src/modules/extra/$extra";
1175                 if (!-e $extrapath) {
1176                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
1177                         next;
1178                 }
1179                 my $source = "src/modules/$extra";
1180                 if (-e $source) {
1181                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
1182                         next;
1183                 }
1184                 # Get dependencies, and add them to be processed.
1185                 my @deps = split / +/, getdependencies($extrapath);
1186                 for my $dep (@deps) {
1187                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
1188                         if (!-e "src/modules/$dep") {
1189                                 if (-e "src/modules/extra/$dep") {
1190                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
1191                                         push @extras, $dep;
1192                                 } else {
1193                                         print STDERR "\e[33;1mWARNING:\e[0m module \e[32;1m$extra\e[0m might be missing dependency \e[32;1m$dep\e[0m - YOU are responsible for satisfying it!\n";
1194                                 }
1195                         }
1196                 }
1197                 print "Enabling $extra ... \n";
1198                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
1199         }
1200 }
1201
1202 sub disable_extras (@)
1203 {
1204         opendir my $dd, "src/modules/extra/";
1205         my @files = readdir($dd);
1206         closedir $dd;
1207         my (@extras) = @_;
1208 EXTRA:  for my $extra (@extras) {
1209                 my $extrapath = "src/modules/extra/$extra";
1210                 my $source = "src/modules/$extra";
1211                 if (!-e $extrapath) {
1212                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
1213                         next;
1214                 }
1215                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
1216                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Source is not a link or doesn't refer to the right file. Remove manually if this is in error.\n";
1217                         next;
1218                 }
1219                 # Check if anything needs this.
1220                 for my $file (@files) {
1221                         my @deps = split / +/, getdependencies("src/modules/extra/$file");
1222                         # File depends on this extra...
1223                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
1224                                 # And is both enabled and not about to be disabled.
1225                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
1226                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
1227                                         next EXTRA;
1228                                 }
1229                         }
1230                 }
1231                 # Now remove.
1232                 print "Disabling $extra ... \n";
1233                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
1234         }
1235 }