4 # InspIRCd -- Internet Relay Chat Daemon
6 # Copyright (C) 2012-2017 Peter Powell <petpow@saberuk.com>
7 # Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
8 # Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
9 # Copyright (C) 2003, 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
10 # Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
11 # Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
12 # Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
13 # Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
14 # Copyright (C) 2003-2006 Craig McLure <craig@chatspike.net>
16 # This file is part of InspIRCd. InspIRCd is free software: you can
17 # redistribute it and/or modify it under the terms of the GNU General Public
18 # License as published by the Free Software Foundation, version 2.
20 # This program is distributed in the hope that it will be useful, but WITHOUT
21 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
25 # You should have received a copy of the GNU General Public License
26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
36 use warnings FATAL => qw(all);
38 use File::Basename qw(basename);
40 use File::Spec::Functions qw(rel2abs);
41 use FindBin qw($RealDir);
42 use Getopt::Long qw(GetOptions);
43 use POSIX qw(getgid getuid);
55 $opt_disable_interactive,
56 $opt_distribution_label,
68 sub enable_extras (@);
70 sub disable_extras (@);
73 my @opt_disableextras;
76 'clean' => \&cmd_clean,
78 'update' => \&cmd_update,
80 'development' => \$opt_development,
81 'disable-interactive' => \$opt_disable_interactive,
82 'distribution-label=s' => \$opt_distribution_label,
83 'binary-dir=s' => \$opt_binary_dir,
84 'config-dir=s' => \$opt_config_dir,
85 'data-dir=s' => \$opt_data_dir,
87 'log-dir=s' => \$opt_log_dir,
88 'manual-dir=s' => \$opt_manual_dir,
89 'module-dir=s' => \$opt_module_dir,
90 'prefix=s' => \$opt_prefix,
91 'socketengine=s' => \$opt_socketengine,
92 'system' => \$opt_system,
95 # TODO: when the modulemanager rewrite is done these should be removed.
96 'disable-extras=s@' => \@opt_disableextras,
97 'enable-extras=s@' => \@opt_enableextras,
98 'list-extras' => sub { list_extras; exit 0; },
101 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
102 @opt_enableextras = split /,/, join(',', @opt_enableextras);
103 @opt_disableextras = split /,/, join(',', @opt_disableextras);
104 enable_extras(@opt_enableextras);
105 disable_extras(@opt_disableextras);
107 print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
111 our $interactive = !(
114 defined $opt_binary_dir ||
115 defined $opt_config_dir ||
116 defined $opt_data_dir ||
117 defined $opt_development ||
118 defined $opt_disable_interactive ||
119 defined $opt_distribution_label ||
121 defined $opt_log_dir ||
122 defined $opt_manual_dir ||
123 defined $opt_module_dir ||
124 defined $opt_prefix ||
125 defined $opt_socketengine ||
126 defined $opt_system ||
130 my %version = get_version $opt_distribution_label;
131 print_format "<|BOLD Configuring InspIRCd $version{FULL} on $^O.|>\n";
135 %config = read_config_file(CONFIGURE_CACHE_FILE);
136 run_test CONFIGURE_CACHE_FILE, %config;
137 if (!defined $config{VERSION}) {
138 $config{VERSION} = CONFIGURE_CACHE_VERSION;
139 } elsif ($config{VERSION} != CONFIGURE_CACHE_VERSION) {
140 print_warning "ignoring contents of ${\CONFIGURE_CACHE_FILE} as it was generated by an incompatible version of $0!";
141 %config = ('VERSION', CONFIGURE_CACHE_VERSION);
145 $config{CXX} = find_compiler($config{CXX} // $ENV{CXX});
146 unless ($config{CXX}) {
147 say 'A suitable C++ compiler could not be detected on your system!';
148 unless ($interactive) {
149 say 'Set the CXX environment variable to the path to a C++ compiler binary if this is incorrect.';
152 until ($config{CXX}) {
153 my $compiler_path = prompt_string 1, 'Please enter the path to a C++ compiler binary:', 'c++';
154 $config{CXX} = find_compiler $compiler_path;
157 my %compiler = get_compiler_info($config{CXX});
159 $config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', $^O eq 'darwin' ? undef : '-lrt');
160 $config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
163 push @socketengines, 'epoll' if run_test 'epoll', test_header $config{CXX}, 'sys/epoll.h';
164 push @socketengines, 'kqueue' if run_test 'kqueue', test_file $config{CXX}, 'kqueue.cpp';
165 push @socketengines, 'ports' if run_test 'Solaris IOCP', test_header $config{CXX}, 'port.h';
166 push @socketengines, 'poll' if run_test 'poll', test_header $config{CXX}, 'poll.h';
167 push @socketengines, 'select';
169 if (defined $opt_socketengine) {
170 unless (grep { $_ eq $opt_socketengine } @socketengines) {
171 my $reason = -f "src/socketengines/socketengine_$opt_socketengine.cpp" ? 'is not available on this platform' : 'does not exist';
172 print_error "The socket engine you requested ($opt_socketengine) $reason!",
173 'Available socket engines are:',
174 map { " * $_" } @socketengines;
177 $config{SOCKETENGINE} = $opt_socketengine // $socketengines[0];
179 if (defined $opt_system) {
180 $config{BASE_DIR} = $opt_prefix // '/var/lib/inspircd';
181 $config{BINARY_DIR} = $opt_binary_dir // '/usr/sbin';
182 $config{CONFIG_DIR} = $opt_config_dir // '/etc/inspircd';
183 $config{DATA_DIR} = $opt_data_dir // '/var/inspircd';
184 $config{LOG_DIR} = $opt_module_dir // '/var/log/inspircd';
185 $config{MANUAL_DIR} = $opt_manual_dir // '/usr/share/man/man1';
186 $config{MODULE_DIR} = $opt_module_dir // '/usr/lib/inspircd';
188 $config{BASE_DIR} = $opt_prefix // $config{BASE_DIR} // rel2abs 'run';
189 $config{BINARY_DIR} = $opt_binary_dir // $config{BINARY_DIR} // rel2abs $config{BASE_DIR} . '/bin';
190 $config{CONFIG_DIR} = $opt_config_dir // $config{CONFIG_DIR} // rel2abs $config{BASE_DIR} . '/conf';
191 $config{DATA_DIR} = $opt_data_dir // $config{DATA_DIR} // rel2abs $config{BASE_DIR} . '/data';
192 $config{LOG_DIR} = $opt_log_dir // $config{LOG_DIR} // rel2abs $config{BASE_DIR} . '/logs';
193 $config{MANUAL_DIR} = $opt_manual_dir // $config{MANUAL_DIR} // rel2abs $config{BASE_DIR} . '/manuals';
194 $config{MODULE_DIR} = $opt_module_dir // $config{MODULE_DIR} // rel2abs $config{BASE_DIR} . '/modules';
197 # Parse --gid=123 or --gid=foo and extract the group id.
199 if (defined $opt_gid) {
200 @group = $opt_gid =~ /^\d+$/ ? getgrgid($opt_gid) : getgrnam($opt_gid);
201 print_error "there is no '$opt_gid' group on this system!" unless @group;
203 @group = $opt_system ? getgrnam('irc') : getgrgid($config{GID} // getgid());
204 print_error "you need to specify a group to run as using '--gid [id|name]'!" unless @group;
206 $config{GROUP} = $group[0];
207 $config{GID} = $group[2];
209 # Parse --uid=123 or --uid=foo and extract the user id.
211 if (defined $opt_uid) {
212 @user = $opt_uid =~ /^\d+$/ ? getpwuid($opt_uid) : getpwnam($opt_uid);
213 print_error "there is no '$opt_uid' user on this system!" unless @user;
215 @user = $opt_system ? getpwnam('irc') : getpwuid($config{UID} // getuid());
216 print_error "you need to specify a user to run as using '--uid [id|name]'!" unless @user;
218 $config{USER} = $user[0];
219 $config{UID} = $user[2];
222 system 'tput', 'clear' if $interactive;
224 # Warn the user about clock drifting when running on OpenVZ.
225 if (-e '/proc/user_beancounters' || -e '/proc/vz/vzaquota') {
226 print_warning <<'EOW';
227 You are building InspIRCd inside of an an OpenVZ container. If you
228 plan to use InspIRCd in this container then you should make sure that NTP is
229 configured on the Hardware Node. Failure to do so may result in clock drifting!
233 # Check that the user actually wants this version.
234 if ($version{LABEL} ne 'release') {
235 print_warning <<'EOW';
236 You are building a development version. This contains code which has
237 not been tested as heavily and may contain various faults which could seriously
238 affect the running of your server. It is recommended that you use a stable
241 You can obtain the latest stable version from http://www.inspircd.org/ or by
242 running `git checkout insp20` if you are installing from Git.
244 if (!prompt_bool $interactive, 'I understand this warning and want to continue anyway.', $opt_development // 0) {
245 say STDERR 'If you understand this warning and still want to continue pass the --development flag.' unless $interactive;
250 # Configure directory settings.
251 my $question = <<"EOQ";
252 Currently, InspIRCd is configured with the following paths:
254 <|BOLD Base:|> $config{BASE_DIR}
255 <|BOLD Binary:|> $config{BINARY_DIR}
256 <|BOLD Config:|> $config{CONFIG_DIR}
257 <|BOLD Data:|> $config{DATA_DIR}
258 <|BOLD Log:|> $config{LOG_DIR}
259 <|BOLD Manual:|> $config{MANUAL_DIR}
260 <|BOLD Module:|> $config{MODULE_DIR}
262 Do you want to change these settings?
264 if (prompt_bool $interactive, $question, 0) {
265 my $original_base_dir = $config{BASE_DIR};
266 $config{BASE_DIR} = prompt_dir $interactive, 'In what directory do you wish to install the InspIRCd base?', $config{BASE_DIR};
267 foreach my $key (qw(BINARY_DIR CONFIG_DIR DATA_DIR LOG_DIR MANUAL_DIR MODULE_DIR)) {
268 $config{$key} =~ s/^\Q$original_base_dir\E/$config{BASE_DIR}/;
270 $config{BINARY_DIR} = prompt_dir $interactive, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR};
271 $config{CONFIG_DIR} = prompt_dir $interactive, 'In what directory are configuration files to be stored?', $config{CONFIG_DIR};
272 $config{DATA_DIR} = prompt_dir $interactive, 'In what directory are variable data files to be stored?', $config{DATA_DIR};
273 $config{LOG_DIR} = prompt_dir $interactive, 'In what directory are log files to be stored?', $config{LOG_DIR};
274 $config{MANUAL_DIR} = prompt_dir $interactive, 'In what directory are manual pages to be placed?', $config{MANUAL_DIR};
275 $config{MODULE_DIR} = prompt_dir $interactive, 'In what directory are modules to be placed?', $config{MODULE_DIR};
278 # Configure module settings.
280 Currently, InspIRCd is configured to automatically enable all available extra modules.
282 Would you like to enable extra modules manually?
284 if (prompt_bool $interactive, $question, 0) {
285 foreach my $extra (<src/modules/extra/m_*.cpp>) {
286 my $module_name = basename $extra, '.cpp';
287 if (prompt_bool $interactive, "Would you like to enable $module_name?", 0) {
288 enable_extras "$module_name.cpp";
292 # TODO: finish modulemanager rewrite and replace this code with:
293 # system './modulemanager', 'enable', '--auto';
294 enable_extras 'm_ssl_gnutls.cpp' unless system 'pkg-config --exists gnutls >/dev/null 2>&1';
295 enable_extras 'm_ssl_mbedtls.cpp' if -e '/usr/include/mbedtls/ssl.h';
296 enable_extras 'm_ssl_openssl.cpp' unless system 'pkg-config --exists openssl >/dev/null 2>&1';
299 # Generate SSL certificates.
300 if (<src/modules/m_ssl_*.cpp> && prompt_bool $interactive, 'Would you like to generate SSL certificates now?', $interactive) {
301 system './tools/genssl', 'auto';
304 # Cache the distribution label so that its not lost when --update is run.
305 $config{DISTRIBUTION} = $opt_distribution_label if $opt_distribution_label;
307 write_configure_cache %config;
308 parse_templates \%config, \%compiler, \%version;
310 print_format <<"EOM";
312 Configuration is complete! You have chosen to build with the following settings:
315 <|GREEN Binary:|> $config{CXX}
316 <|GREEN Name:|> $compiler{NAME}
317 <|GREEN Version:|> $compiler{VERSION}
319 <|GREEN Extra Modules:|>
322 for my $file (<src/modules/m_*>) {
323 my $module = basename $file, '.cpp';
324 say " * $module" if -l $file;
327 print_format <<"EOM";
330 <|GREEN Base:|> $config{BASE_DIR}
331 <|GREEN Binary:|> $config{BINARY_DIR}
332 <|GREEN Config:|> $config{CONFIG_DIR}
333 <|GREEN Data:|> $config{DATA_DIR}
334 <|GREEN Log:|> $config{LOG_DIR}
335 <|GREEN Manual:|> $config{MANUAL_DIR}
336 <|GREEN Module:|> $config{MODULE_DIR}
338 <|GREEN Execution Group:|> $config{GROUP} ($config{GID})
339 <|GREEN Execution User:|> $config{USER} ($config{UID})
340 <|GREEN Socket Engine:|> $config{SOCKETENGINE}
342 To build with these settings run '<|GREEN make -j${\get_cpu_count}|>' now.
346 # Routine to list out the extra/ modules that have been enabled.
347 # Note: when getting any filenames out and comparing, it's important to lc it if the
348 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
349 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
350 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
354 my $srcdir = File::Spec->catdir("src", "modules");
355 my $abs_srcdir = File::Spec->rel2abs($srcdir);
358 opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
359 my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
362 opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
363 my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
366 my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
368 EXTRA: for my $extra (@extras) {
369 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
370 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
371 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
372 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
373 if (-l $abs_source) {
374 # Symlink, is it in the right place?
375 my $targ = readlink($abs_source);
376 my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
377 if ($abs_targ eq $abs_extra) {
378 $extras{$extra} = "\e[32;1menabled\e[0m";
380 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
382 } elsif (-e $abs_source) {
383 my ($devext, $inoext) = stat($abs_extra);
384 my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
386 if ($devsrc == $devext && $inosrc == $inoext) {
387 $extras{$extra} = "\e[32;1menabled\e[0m";
389 $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
392 open my $extfd, "<", $abs_extra;
393 open my $srcfd, "<", $abs_source;
395 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
396 $extras{$extra} = "\e[32;1menabled\e[0m";
398 $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
402 $extras{$extra} = "\e[33;1mdisabled\e[0m";
405 # Now let's add dependency info
406 for my $extra (keys(%extras)) {
407 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
408 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
409 my @deps = split /\s+/, get_directive($abs_extra, 'ModDep', '');
410 for my $dep (@deps) {
411 if (exists($extras{$dep})) {
412 my $ref = \$extras{$dep}; # Take reference.
413 if ($$ref !~ m/needed by/) {
414 # First dependency found.
415 if ($$ref =~ m/enabled/) {
416 $$ref .= " (needed by \e[32;1m$extra\e[0m";
418 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
419 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
422 if ($$ref =~ m/enabled/) {
423 $$ref .= ", \e[32;1m$extra\e[0m";
425 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
431 for my $extra (sort {$a cmp $b} keys(%extras)) {
432 my $text = $extras{$extra};
433 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
434 printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
436 printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
439 return keys(%extras) if wantarray; # Can be used by manage_extras.
442 sub enable_extras (@) {
444 for my $extra (@extras) {
445 my $extrapath = "src/modules/extra/$extra";
446 if (!-e $extrapath) {
447 print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
450 my $source = "src/modules/$extra";
452 print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
455 # Get dependencies, and add them to be processed.
456 my @deps = split /\s+/, get_directive($extrapath, 'ModDep', '');
457 for my $dep (@deps) {
458 next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
459 if (!-e "src/modules/$dep" && !-e "include/$dep") {
460 if (-e "src/modules/extra/$dep") {
461 print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
464 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";
468 print "Enabling $extra ... \n";
469 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
473 sub disable_extras (@)
475 opendir my $dd, "src/modules/extra/";
476 my @files = readdir($dd);
479 EXTRA: for my $extra (@extras) {
480 my $extrapath = "src/modules/extra/$extra";
481 my $source = "src/modules/$extra";
482 if (!-e $extrapath) {
483 print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
486 if ((! -l $source) || readlink($source) ne "extra/$extra") {
487 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";
490 # Check if anything needs this.
491 for my $file (@files) {
492 my @deps = split /\s+/, get_directive("src/modules/extra/$file", 'ModDep', '');
493 # File depends on this extra...
494 if (scalar(grep { $_ eq $extra } @deps) > 0) {
495 # And is both enabled and not about to be disabled.
496 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
497 print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
503 print "Disabling $extra ... \n";
504 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";