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');
162 if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
163 $config{SOCKETENGINE} //= 'epoll';
166 if ($config{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
167 $config{SOCKETENGINE} //= 'kqueue';
170 if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
171 $config{SOCKETENGINE} //= 'ports';
174 if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
175 $config{SOCKETENGINE} //= 'poll';
178 # Select is available on all platforms
179 $config{HAS_SELECT} = 1;
180 $config{SOCKETENGINE} //= 'select';
182 if (defined $opt_socketengine) {
183 my $cfgkey = 'HAS_' . uc $opt_socketengine;
184 if ($config{$cfgkey} && -f "src/socketengines/socketengine_$opt_socketengine.cpp") {
185 $config{SOCKETENGINE} = $opt_socketengine;
187 print "Unable to use a socket engine which is not supported on this platform ($opt_socketengine)!\n";
188 print "Available socket engines are:";
189 foreach (<src/socketengines/socketengine_*.cpp>) {
190 s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
191 print " $1" if $config{'HAS_' . uc $1};
198 if (defined $opt_system) {
199 $config{BASE_DIR} = $opt_prefix // '/var/lib/inspircd';
200 $config{BINARY_DIR} = $opt_binary_dir // '/usr/sbin';
201 $config{CONFIG_DIR} = $opt_config_dir // '/etc/inspircd';
202 $config{DATA_DIR} = $opt_data_dir // '/var/inspircd';
203 $config{LOG_DIR} = $opt_module_dir // '/var/log/inspircd';
204 $config{MANUAL_DIR} = $opt_manual_dir // '/usr/share/man/man1';
205 $config{MODULE_DIR} = $opt_module_dir // '/usr/lib/inspircd';
207 $config{BASE_DIR} = $opt_prefix // $config{BASE_DIR} // rel2abs 'run';
208 $config{BINARY_DIR} = $opt_binary_dir // $config{BINARY_DIR} // rel2abs $config{BASE_DIR} . '/bin';
209 $config{CONFIG_DIR} = $opt_config_dir // $config{CONFIG_DIR} // rel2abs $config{BASE_DIR} . '/conf';
210 $config{DATA_DIR} = $opt_data_dir // $config{DATA_DIR} // rel2abs $config{BASE_DIR} . '/data';
211 $config{LOG_DIR} = $opt_log_dir // $config{LOG_DIR} // rel2abs $config{BASE_DIR} . '/logs';
212 $config{MANUAL_DIR} = $opt_manual_dir // $config{MANUAL_DIR} // rel2abs $config{BASE_DIR} . '/manuals';
213 $config{MODULE_DIR} = $opt_module_dir // $config{MODULE_DIR} // rel2abs $config{BASE_DIR} . '/modules';
216 # Parse --gid=123 or --gid=foo and extract the group id.
218 if (defined $opt_gid) {
219 @group = $opt_gid =~ /^\d+$/ ? getgrgid($opt_gid) : getgrnam($opt_gid);
220 print_error "there is no '$opt_gid' group on this system!" unless @group;
222 @group = $opt_system ? getgrnam('irc') : getgrgid($config{GID} // getgid());
223 print_error "you need to specify a group to run as using '--gid [id|name]'!" unless @group;
225 $config{GROUP} = $group[0];
226 $config{GID} = $group[2];
228 # Parse --uid=123 or --uid=foo and extract the user id.
230 if (defined $opt_uid) {
231 @user = $opt_uid =~ /^\d+$/ ? getpwuid($opt_uid) : getpwnam($opt_uid);
232 print_error "there is no '$opt_uid' user on this system!" unless @user;
234 @user = $opt_system ? getpwnam('irc') : getpwuid($config{UID} // getuid());
235 print_error "you need to specify a user to run as using '--uid [id|name]'!" unless @user;
237 $config{USER} = $user[0];
238 $config{UID} = $user[2];
241 system 'tput', 'clear' if $interactive;
243 # Warn the user about clock drifting when running on OpenVZ.
244 if (-e '/proc/user_beancounters' || -e '/proc/vz/vzaquota') {
245 print_warning <<'EOW';
246 You are building InspIRCd inside of an an OpenVZ container. If you
247 plan to use InspIRCd in this container then you should make sure that NTP is
248 configured on the Hardware Node. Failure to do so may result in clock drifting!
252 # Check that the user actually wants this version.
253 if ($version{LABEL} ne 'release') {
254 print_warning <<'EOW';
255 You are building a development version. This contains code which has
256 not been tested as heavily and may contain various faults which could seriously
257 affect the running of your server. It is recommended that you use a stable
260 You can obtain the latest stable version from http://www.inspircd.org/ or by
261 running `git checkout insp20` if you are installing from Git.
263 if (!prompt_bool $interactive, 'I understand this warning and want to continue anyway.', $opt_development // 0) {
264 say STDERR 'If you understand this warning and still want to continue pass the --development flag.' unless $interactive;
269 # Configure directory settings.
270 my $question = <<"EOQ";
271 Currently, InspIRCd is configured with the following paths:
273 <|BOLD Base:|> $config{BASE_DIR}
274 <|BOLD Binary:|> $config{BINARY_DIR}
275 <|BOLD Config:|> $config{CONFIG_DIR}
276 <|BOLD Data:|> $config{DATA_DIR}
277 <|BOLD Log:|> $config{LOG_DIR}
278 <|BOLD Manual:|> $config{MANUAL_DIR}
279 <|BOLD Module:|> $config{MODULE_DIR}
281 Do you want to change these settings?
283 if (prompt_bool $interactive, $question, 0) {
284 my $original_base_dir = $config{BASE_DIR};
285 $config{BASE_DIR} = prompt_dir $interactive, 'In what directory do you wish to install the InspIRCd base?', $config{BASE_DIR};
286 foreach my $key (qw(BINARY_DIR CONFIG_DIR DATA_DIR LOG_DIR MANUAL_DIR MODULE_DIR)) {
287 $config{$key} =~ s/^\Q$original_base_dir\E/$config{BASE_DIR}/;
289 $config{BINARY_DIR} = prompt_dir $interactive, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR};
290 $config{CONFIG_DIR} = prompt_dir $interactive, 'In what directory are configuration files to be stored?', $config{CONFIG_DIR};
291 $config{DATA_DIR} = prompt_dir $interactive, 'In what directory are variable data files to be stored?', $config{DATA_DIR};
292 $config{LOG_DIR} = prompt_dir $interactive, 'In what directory are log files to be stored?', $config{LOG_DIR};
293 $config{MANUAL_DIR} = prompt_dir $interactive, 'In what directory are manual pages to be placed?', $config{MANUAL_DIR};
294 $config{MODULE_DIR} = prompt_dir $interactive, 'In what directory are modules to be placed?', $config{MODULE_DIR};
297 # Configure module settings.
299 Currently, InspIRCd is configured to automatically enable all available extra modules.
301 Would you like to enable extra modules manually?
303 if (prompt_bool $interactive, $question, 0) {
304 foreach my $extra (<src/modules/extra/m_*.cpp>) {
305 my $module_name = basename $extra, '.cpp';
306 if (prompt_bool $interactive, "Would you like to enable $module_name?", 0) {
307 enable_extras "$module_name.cpp";
311 # TODO: finish modulemanager rewrite and replace this code with:
312 # system './modulemanager', 'enable', '--auto';
313 enable_extras 'm_ssl_gnutls.cpp' unless system 'pkg-config --exists gnutls >/dev/null 2>&1';
314 enable_extras 'm_ssl_mbedtls.cpp' if -e '/usr/include/mbedtls/ssl.h';
315 enable_extras 'm_ssl_openssl.cpp' unless system 'pkg-config --exists openssl >/dev/null 2>&1';
318 # Generate SSL certificates.
319 if (<src/modules/m_ssl_*.cpp> && prompt_bool $interactive, 'Would you like to generate SSL certificates now?', $interactive) {
320 system './tools/genssl', 'auto';
323 # Cache the distribution label so that its not lost when --update is run.
324 $config{DISTRIBUTION} = $opt_distribution_label if $opt_distribution_label;
326 write_configure_cache %config;
327 parse_templates \%config, \%compiler, \%version;
329 print_format <<"EOM";
331 Configuration is complete! You have chosen to build with the following settings:
334 <|GREEN Binary:|> $config{CXX}
335 <|GREEN Name:|> $compiler{NAME}
336 <|GREEN Version:|> $compiler{VERSION}
338 <|GREEN Extra Modules:|>
341 for my $file (<src/modules/m_*>) {
342 my $module = basename $file, '.cpp';
343 say " * $module" if -l $file;
346 print_format <<"EOM";
349 <|GREEN Base:|> $config{BASE_DIR}
350 <|GREEN Binary:|> $config{BINARY_DIR}
351 <|GREEN Config:|> $config{CONFIG_DIR}
352 <|GREEN Data:|> $config{DATA_DIR}
353 <|GREEN Log:|> $config{LOG_DIR}
354 <|GREEN Manual:|> $config{MANUAL_DIR}
355 <|GREEN Module:|> $config{MODULE_DIR}
357 <|GREEN Execution Group:|> $config{GROUP} ($config{GID})
358 <|GREEN Execution User:|> $config{USER} ($config{UID})
359 <|GREEN Socket Engine:|> $config{SOCKETENGINE}
361 To build with these settings run '<|GREEN make -j${\get_cpu_count}|>' now.
365 # Routine to list out the extra/ modules that have been enabled.
366 # Note: when getting any filenames out and comparing, it's important to lc it if the
367 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
368 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
369 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
373 my $srcdir = File::Spec->catdir("src", "modules");
374 my $abs_srcdir = File::Spec->rel2abs($srcdir);
377 opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
378 my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
381 opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
382 my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
385 my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
387 EXTRA: for my $extra (@extras) {
388 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
389 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
390 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
391 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
392 if (-l $abs_source) {
393 # Symlink, is it in the right place?
394 my $targ = readlink($abs_source);
395 my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
396 if ($abs_targ eq $abs_extra) {
397 $extras{$extra} = "\e[32;1menabled\e[0m";
399 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
401 } elsif (-e $abs_source) {
402 my ($devext, $inoext) = stat($abs_extra);
403 my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
405 if ($devsrc == $devext && $inosrc == $inoext) {
406 $extras{$extra} = "\e[32;1menabled\e[0m";
408 $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
411 open my $extfd, "<", $abs_extra;
412 open my $srcfd, "<", $abs_source;
414 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
415 $extras{$extra} = "\e[32;1menabled\e[0m";
417 $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
421 $extras{$extra} = "\e[33;1mdisabled\e[0m";
424 # Now let's add dependency info
425 for my $extra (keys(%extras)) {
426 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
427 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
428 my @deps = split /\s+/, get_directive($abs_extra, 'ModDep', '');
429 for my $dep (@deps) {
430 if (exists($extras{$dep})) {
431 my $ref = \$extras{$dep}; # Take reference.
432 if ($$ref !~ m/needed by/) {
433 # First dependency found.
434 if ($$ref =~ m/enabled/) {
435 $$ref .= " (needed by \e[32;1m$extra\e[0m";
437 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
438 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
441 if ($$ref =~ m/enabled/) {
442 $$ref .= ", \e[32;1m$extra\e[0m";
444 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
450 for my $extra (sort {$a cmp $b} keys(%extras)) {
451 my $text = $extras{$extra};
452 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
453 printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
455 printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
458 return keys(%extras) if wantarray; # Can be used by manage_extras.
461 sub enable_extras (@) {
463 for my $extra (@extras) {
464 my $extrapath = "src/modules/extra/$extra";
465 if (!-e $extrapath) {
466 print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
469 my $source = "src/modules/$extra";
471 print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
474 # Get dependencies, and add them to be processed.
475 my @deps = split /\s+/, get_directive($extrapath, 'ModDep', '');
476 for my $dep (@deps) {
477 next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
478 if (!-e "src/modules/$dep" && !-e "include/$dep") {
479 if (-e "src/modules/extra/$dep") {
480 print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
483 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";
487 print "Enabling $extra ... \n";
488 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
492 sub disable_extras (@)
494 opendir my $dd, "src/modules/extra/";
495 my @files = readdir($dd);
498 EXTRA: for my $extra (@extras) {
499 my $extrapath = "src/modules/extra/$extra";
500 my $source = "src/modules/$extra";
501 if (!-e $extrapath) {
502 print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
505 if ((! -l $source) || readlink($source) ne "extra/$extra") {
506 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";
509 # Check if anything needs this.
510 for my $file (@files) {
511 my @deps = split /\s+/, get_directive("src/modules/extra/$file", 'ModDep', '');
512 # File depends on this extra...
513 if (scalar(grep { $_ eq $extra } @deps) > 0) {
514 # And is both enabled and not about to be disabled.
515 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
516 print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
522 print "Disabling $extra ... \n";
523 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";