File Coverage

blib/lib/MySQL/Sandbox.pm
Criterion Covered Total %
statement 44 375 11.7
branch 9 202 4.4
condition 1 66 1.5
subroutine 10 37 27.0
pod 0 27 0.0
total 64 707 9.0


line stmt bran cond sub pod time code
1             package MySQL::Sandbox;
2 24     24   84241 use strict;
  24         151  
  24         622  
3 24     24   106 use warnings;
  24         39  
  24         579  
4 24     24   118 use Carp;
  24         42  
  24         1573  
5 24     24   6487 use English qw( -no_match_vars );
  24         60336  
  24         124  
6 24     24   14281 use Socket;
  24         80984  
  24         8334  
7 24     24   173 use File::Find;
  24         41  
  24         1091  
8 24     24   8809 use Data::Dumper;
  24         120100  
  24         1487  
9              
10 24     24   163 use base qw( Exporter);
  24         40  
  24         9039  
11             our @ISA= qw(Exporter);
12             our @EXPORT_OK= qw( is_port_open
13             runs_as_root
14             mylogin_cnf_exists
15             exists_in_path
16             is_a_sandbox
17             find_safe_port_and_directory
18             first_unused_port
19             get_sandbox_params
20             is_sandbox_running
21             get_sb_info
22             get_ports
23             get_ranges
24             use_env
25             sbinstr
26             get_json_from_dirs
27             get_option_file_contents
28             validate_json_object
29             fix_server_uuid
30             greater_version
31             split_version
32             ) ;
33              
34             our $VERSION=q{3.2.15};
35             our $DEBUG;
36              
37             BEGIN {
38 24   50 24   268 $DEBUG = $ENV{'SBDEBUG'} || $ENV{'SBVERBOSE'} || 0;
39 24 50       75 if (! $ENV{'USER'})
40             {
41 24         53143 my $user = qx/whoami/;
42 24         282 chomp $user;
43 24         495 $ENV{'USER'} = $user;
44             }
45 24         132 for my $var (qw( HOME USER PWD ))
46             {
47 72 50       315 unless ($ENV{$var}) {
48 0         0 die "The variable \$$var is undefined - aborting\n";
49             }
50             }
51 24 50       192 if ($ENV{HOME} =~ /\s/)
52             {
53 0         0 die "# The variable \$HOME contains spaces. Please fix this problem before continuing\n(HOME='$ENV{HOME}')\n";
54             }
55 24 50       125 unless ( $ENV{SANDBOX_HOME} ) {
56 24         206 $ENV{SANDBOX_HOME} = "$ENV{HOME}/sandboxes";
57             }
58              
59 24 50       110 unless ($ENV{TMPDIR})
60             {
61 24         140 $ENV{TMPDIR} = '/tmp';
62             }
63 24 50       520 unless ( -d $ENV{TMPDIR})
64             {
65 0         0 die "could not find $ENV{TMPDIR}\n";
66             }
67              
68 24 50       253 if ( -d "$ENV{HOME}/sandboxes" ) {
69 0   0     0 $ENV{SANDBOX_HOME} = $ENV{SANDBOX_HOME} || "$ENV{HOME}/sandboxes";
70             }
71              
72 24 50       204 unless ( $ENV{SANDBOX_BINARY} ) {
73 24 50       245 if ( -d "$ENV{HOME}/opt/mysql") {
74 0         0 $ENV{SANDBOX_BINARY} = "$ENV{HOME}/opt/mysql";
75             }
76             else
77             {
78 24         91785 $ENV{SANDBOX_BINARY} = '';
79             }
80             }
81             }
82              
83             my @supported_versions = qw( 3.23 4.0 4.1 5.0 5.1 5.2 5.3 5.4
84             5.5 5.6 5.7 6.0 8.0 10.0 10.1 10.2 10.3 );
85              
86             our $sandbox_options_file = "my.sandbox.cnf";
87             # our $sandbox_current_options = "current_options.conf";
88              
89             our %default_base_port = (
90             replication => 11000,
91             circular => 14000,
92             multiple => 7000,
93             custom => 5000,
94             );
95              
96             our %default_users = (
97             db_user => 'msandbox',
98             remote_access => '127.%',
99             db_password => 'msandbox',
100             ro_user => 'msandbox_ro',
101             rw_user => 'msandbox_rw',
102             repl_user => 'rsandbox',
103             repl_password => 'rsandbox',
104             );
105              
106             our $SBINSTR_SH_TEXT =<<'SBINSTR_SH_TEXT';
107             if [ -f "$SBINSTR" ]
108             then
109             echo "[`basename $0`] - `date "+%Y-%m-%d %H:%M:%S"` - $@" >> $SBINSTR
110             fi
111             SBINSTR_SH_TEXT
112              
113              
114              
115             sub new {
116 0     0 0 0 my ($class) = @_;
117 0         0 my $self = bless {
118             parse_options => undef,
119             options => undef,
120             }, $class;
121             # my $version = get_version( $install_dir);
122             # $self->{version} = $VERSION;
123 0         0 return $self;
124             }
125              
126             sub parse_options {
127 0     0 0 0 my ($self, $opt ) = @_;
128             # print "<", ref($opt) , ">\n";
129 0 0       0 unless (ref($opt) eq 'HASH') {
130 0         0 confess "parse_options must be a hash reference\n";
131             }
132 0 0       0 if ($opt) {
133 0         0 $self->{parse_options} = $opt;
134             }
135 0         0 my %options = map { $_ , $opt->{$_}{'value'}} keys %{$opt};
  0         0  
  0         0  
136 0         0 $self->{options} = \%options;
137              
138 0         0 return $self->{options};
139             }
140              
141             sub find_safe_port_and_directory {
142 0     0 0 0 my ($wanted_port, $wanted_dir, $upper_directory) = @_;
143 0         0 my $chosen_port = $wanted_port;
144 0         0 my ($ports, undef) = get_sb_info( $ENV{SANDBOX_HOME}, undef);
145             # print Dumper($ports);
146 0   0     0 while ( is_port_open($chosen_port) or exists $ports->{$chosen_port}) {
147 0         0 $chosen_port++;
148 0         0 $chosen_port = first_unused_port($chosen_port);
149             # print "checking -> $chosen_port\n";
150             }
151 0         0 my $suffix = 'a';
152 0         0 my $chosen_dir = $wanted_dir;
153 0         0 while ( -d "$upper_directory/$chosen_dir" ) {
154             # print "checking -> $chosen_dir\n";
155 0         0 $chosen_dir = $wanted_dir . '_' . $suffix;
156 0         0 $suffix++;
157             }
158 0         0 return ($chosen_port, $chosen_dir);
159             }
160              
161             sub get_help {
162 0     0 0 0 my ($self, $msg) = @_;
163 0 0       0 if ($msg) {
164 0         0 warn "[***] $msg\n\n";
165             }
166              
167 0         0 my $HELP_MSG = q{};
168 0         0 for my $op (
169 0         0 sort { $self->{parse_options}->{$a}{so} <=> $self->{parse_options}->{$b}{so} }
170 0         0 grep { $self->{parse_options}->{$_}{parse}} keys %{ $self->{parse_options} } ) {
  0         0  
171 0         0 my $param = $self->{parse_options}->{$op}{parse};
172 0         0 my $param_str = q{ };
173 0         0 my ($short, $long ) = $param =~ / (?: (\w) \| )? (\S+) /x;
174 0 0       0 if ($short) {
175 0         0 $param_str .= q{-} . $short . q{ };
176             }
177 0         0 $long =~ s/ = s \@? / = name/x;
178 0         0 $long =~ s/ = i / = number/x;
179 0         0 $param_str .= q{--} . $long;
180 0         0 my $lparam = 40 - length($param_str);
181 0 0       0 if ($lparam < 0)
182             {
183 0         0 $lparam = 0;
184             }
185 0         0 $param_str .= (q{ } x $lparam );
186 0         0 my $text_items = $self->{parse_options}->{$op}{help};
187 0         0 for my $titem (@{$text_items}) {
  0         0  
188 0         0 $HELP_MSG .= $param_str . $titem . "\n";
189 0         0 $param_str = q{ } x 40;
190             }
191 0 0       0 if (@{$text_items} > 1) {
  0         0  
192 0         0 $HELP_MSG .= "\n";
193             }
194             # $HELP_MSG .= "\n";
195             }
196              
197             my $VAR_HELP =
198             "\nVARIABLES affecting this program: \n"
199             . "\t\$SBDEBUG : DEBUG LEVEL ("
200             . ($ENV{SBDEBUG} || 0) . ")\n"
201             . "\t\$SBVERBOSE : DEBUG LEVEL (same as \$SBDEBUG) ("
202             . ($ENV{SBVERBOSE} || 0) . ")\n"
203              
204             . "\t\$SANDBOX_HOME : root of all sandbox installations ("
205             . use_env($ENV{SANDBOX_HOME}) . ")\n"
206              
207             . "\t\$SANDBOX_BINARY : where to search for binaries ("
208 0   0     0 . use_env($ENV{SANDBOX_BINARY}) . ")\n"
      0        
209             ;
210              
211 0 0       0 if ( $PROGRAM_NAME =~ /replication|multiple/ ) {
212             $VAR_HELP .=
213             "\t\$NODE_OPTIONS : options to pass to all node installations ("
214 0   0     0 . ($ENV{NODE_OPTIONS} || '') . ")\n"
215             }
216              
217 0 0       0 if ( $PROGRAM_NAME =~ /replication/ ) {
218             $VAR_HELP .=
219             "\t\$MASTER_OPTIONS : options to pass to the master installation ("
220             . ($ENV{MASTER_OPTIONS} || '') . ")\n"
221              
222             . "\t\$SLAVE_OPTIONS : options to pass to all slave installations ("
223 0   0     0 . ($ENV{SLAVE_OPTIONS} || '' ) . ")\n"
      0        
224             }
225 0         0 my $target = '';
226 0 0       0 if ( grep {$PROGRAM_NAME =~ /$_/ }
  0         0  
227             qw( make_sandbox make_replication_sandbox
228             make_multiple_sandbox make_multiple_sandbox ) )
229             {
230 0         0 $target = '{tarball|dir|version}';
231 0         0 $HELP_MSG =
232             "tarball = the full path to a MySQL binary tarball\n"
233             . "dir = the path to an expanded MySQL binary tarball\n"
234             . "version = the simple version number of the expanded tarball\n"
235             . " if it is under \$SANDBOX_BINARY and renamed as the\n "
236             . " version number.\n\n"
237             . $HELP_MSG;
238             }
239            
240 0         0 print $self->credits(),
241             "syntax: $PROGRAM_NAME [options] $target \n",
242             $HELP_MSG,
243             $VAR_HELP;
244             # This example is only relevant for a single sandbox, but it is
245             # wrong for a multiple sandbox.
246             #,
247             #"\nExample:\n",
248             #" $PROGRAM_NAME --my_file=large --sandbox_directory=my_sandbox\n\n";
249              
250 0         0 exit(1);
251             }
252              
253             sub credits {
254 1     1 0 2 my ($self) = @_;
255 1         3 my $CREDITS =
256             qq( The MySQL Sandbox, version $VERSION\n)
257             . qq( (C) 2006-2017 Giuseppe Maxia\n);
258 1         21 return $CREDITS;
259             }
260              
261             sub split_version
262             {
263 0     0 0   my ($v) = @_;
264             #if ($v =~ /(?\d+)\.(?\d+)\.(?\d+)/ )
265 0 0         if ($v =~ /(\d+)\.(\d+)\.(\d+)/ )
266             {
267             #return ($+{major}, $+{minor}, $+{rev})
268 0           return ($1, $2, $3)
269              
270             }
271             else
272             {
273 0           die "# Split version: could not get components from <$v>\n";
274             }
275             }
276              
277             sub greater_version
278             {
279 0     0 0   my ($v1, $v2) = @_;
280              
281 0           my ($v1_major, $v1_minor, $v1_rev) = split_version($v1);
282 0           my ($v2_major, $v2_minor, $v2_rev) = split_version($v2);
283 0 0 0       if ( $v1_major > $v2_major)
    0 0        
    0 0        
284             {
285 0           return 1;
286             }
287             elsif ( ($v1_major == $v2_major) && ($v1_minor > $v2_minor))
288             {
289 0           return 1;
290             }
291             elsif ( ($v1_major == $v2_major) && ($v1_minor == $v2_minor) && ($v1_rev > $v2_rev) )
292             {
293 0           return 1;
294             }
295 0           return 0
296             }
297              
298              
299             sub fix_server_uuid
300             {
301 0     0 0   my ($server_id, $version, $port, $sandbox_directory) = @_;
302 0 0         if ($version =~ /(\d+)\.(\d+)/)
303             {
304 0           my ($major, $minor ) = ($1, $2);
305 0 0 0       unless ( ($major == 8) or ( ($major == 5) && ($minor >=6)) )
      0        
306             {
307 0           return;
308             }
309             }
310 0           my $current_dir = $ENV{PWD};
311 0           my $increase_id = 0;
312 0           $sandbox_directory =~ s{/$}{};
313 0           my $operation_dir= "$sandbox_directory/data";
314 0 0         if ( ! -d $operation_dir)
315             {
316 0           die "<$operation_dir> not found\n";
317             }
318 0           chdir $operation_dir;
319 0 0         print "# operation_dir is $operation_dir\n" if $DEBUG;
320 0 0 0       if ( ($operation_dir =~ m{/node\d/data$}) && (-d "../../master"))
321             {
322 0           $increase_id =1;
323             }
324             # 12345678 1234 1234 1234 123456789012
325             # my $new_uuid='00000000-0000-0000-0000-000000000000';
326 0           my $group1 = sprintf('%08d', $port);
327              
328 0           my $group2= sprintf('%04d-%04d-%04d-%012d', 0,0,0,0);
329 0 0 0       if ($server_id < 10)
    0          
330             {
331 0           $group2 =~ s/\d/$server_id/g;
332             }
333             elsif (($server_id >= 100) && ($server_id < 109))
334             {
335 0           $server_id -= 100;
336 0 0         $server_id += 1 if $increase_id; # 101 => 2
337 0           $group2 =~ s/\d/$server_id/g;
338             }
339             else
340             {
341 0           my $second_id = $server_id;
342 0 0         if ($second_id > 9999)
343             {
344 0           $second_id = 9999;
345             }
346 0           $group2 = sprintf( '%04d-%04d-%04d-%012d', $second_id, $second_id, $second_id, $server_id );
347             }
348 0           my $new_uuid= "$group1-$group2";
349 0 0         open my $FH, '>', 'auto.cnf'
350             or die "Error updating 'auto.cnf' ($!)\n";
351 0           print $FH "[auto]\n";
352 0           print $FH "server-uuid=$new_uuid\n";
353 0           close $FH;
354 0           chdir $current_dir;
355 0 0         print "New UUID=$new_uuid\n" if $DEBUG;
356             }
357              
358             sub validate_json_object {
359 0     0 0   my ($json_filename, $json_text) = @_;
360              
361 0           my $JSON_module = undef;
362 0           for my $module ( 'JSON', 'JSON::PP', 'JSON::XS')
363             {
364 0           eval "use $module;";
365 0 0         if (! $@)
366             {
367 0 0         print "# Using $module\n" if $DEBUG;
368 0           $JSON_module=$module;
369 0           last;
370             }
371             }
372 0 0         unless ($JSON_module)
373             {
374 0 0         print "# JSON modules not installed - skipped evaluation\n" if $DEBUG;
375 0           return -1;
376             }
377              
378 0 0         unless ($json_text)
379             {
380 0           $json_text = slurp($json_filename);
381             }
382 0           my $json = $JSON_module->new->allow_nonref;
383              
384 0           my $perl_value;
385 0           eval {
386 0           $perl_value = $json->decode( $json_text );
387             };
388 0 0         if ($@)
389             {
390 0 0         print "error decoding json object\n" if $DEBUG;
391 0           return ;
392             }
393 0           return 1;
394             }
395              
396             sub slurp {
397 0     0 0   my ($filename, $skip_blanks, $skip_comments ) = @_;
398 0 0         open my $FH , q{<}, $filename
399             or die "file '$filename' not found\n";
400 0           my @text_array = ();
401 0           my $text='';
402 0           while (my $line = <$FH>)
403             {
404 0 0         if ($skip_blanks)
405             {
406 0 0         next if $line =~ /^\s*$/;
407             }
408 0 0         if ($skip_comments)
409             {
410 0 0         next if $line =~ /^\s*#/;
411             }
412 0 0         if (wantarray)
413             {
414 0           push @text_array, $line;
415             }
416             else
417             {
418 0           $text .= $line;
419             }
420             }
421 0           close $FH;
422 0 0         if (wantarray)
423             {
424 0           return @text_array;
425             }
426             else
427             {
428 0           return $text;
429             }
430             }
431              
432             sub get_json_from_dirs {
433 0     0 0   my ($directories, $json_file) = @_;
434 0           my $collective_json = '';
435 0           my $indent = ' ';
436 0           for my $dir (@$directories)
437             {
438 0           my $filename = "$dir/$json_file";
439 0 0         if ($collective_json)
440             {
441 0           $collective_json .= ",\n"
442             }
443             else
444             {
445 0           $collective_json = "{\n";
446             }
447 0           $collective_json .= qq("$dir": \n);
448 0 0         if ( -f $filename)
449             {
450             # get the contents
451 0           my @json_lines = slurp($filename, 'skip_blanks');
452 0           for my $jl (@json_lines)
453             {
454 0           $collective_json .= $indent . $jl;
455             }
456             }
457             else
458             {
459 0 0         if ($DEBUG)
460             {
461 0           warn "# No connection.json found in $dir\n";
462 0           my ($package, $filename, $line) = caller;
463 0           warn "# called from $package - $filename - $line \n";
464             }
465 0           $collective_json .= "{}";
466             }
467             }
468 0           $collective_json .= "}";
469 0           my $is_valid_json = validate_json_object(undef, $collective_json);
470 0 0 0       if ($is_valid_json && ($is_valid_json == -1))
    0          
471             {
472 0 0         if ($DEBUG)
473             {
474 0           warn "# Could not validate JSON object\n";
475             }
476             }
477             elsif ( ! $is_valid_json)
478             {
479 0           warn "Invalid JSON object in $ENV{PWD} from [@$directories] \n";
480 0           $collective_json = qq({ "comment": "WARNING: invalid JSON object", "original" : )
481             . $collective_json
482             . "\n}";
483             }
484 0           return $collective_json;
485             }
486              
487             #sub get_version {
488             # my ($install_dir) = @_;
489             # open my $VER , q{<}, "$install_dir/VERSION"
490             # #open my $VER , q{<}, "VERSION"
491             # or die "file 'VERSION' not found\n";
492             # my $version = <$VER>;
493             # chomp $version;
494             # close $VER;
495             # return $version;
496             #}
497              
498             sub write_to {
499 0     0 0   my ($self, $fname, $mode, $contents) = @_;
500 0 0         open my $FILE, $mode, $fname
501             or die "can't open file $fname\n";
502 0           print $FILE $contents, "\n";
503 0 0 0       if (($mode eq '>') && ( $contents =~ m/\#!\/bin\/sh/ ) ) {
504 0           print $FILE $SBINSTR_SH_TEXT;
505             }
506 0           close $FILE;
507             }
508              
509             sub supported_versions {
510 0     0 0   return \@supported_versions;
511             }
512              
513             sub is_port_open {
514 0     0 0   my ($port) = @_;
515 0 0         die "No port" unless $port;
516 0           my ($host, $iaddr, $paddr, $proto);
517              
518 0           $host = '127.0.0.1';
519 0 0         $iaddr = inet_aton($host)
520             or die "no host: $host";
521 0           $paddr = sockaddr_in($port, $iaddr);
522              
523 0           $proto = getprotobyname('tcp');
524 0 0         socket(SOCK, PF_INET, SOCK_STREAM, $proto)
525             or die "error creating test socket for port $port: $!";
526 0 0         if (connect(SOCK, $paddr)) {
527 0 0         close (SOCK)
528             or die "error closing test socket: $!";
529 0           return 1;
530             }
531 0           return 0;
532             }
533              
534             sub first_unused_port {
535 0     0 0   my ($port) = @_;
536 0           while (is_port_open($port)) {
537 0           $port++;
538 0 0         if ($port > 0xFFF0) {
539 0           die "no ports available\n";
540             }
541             }
542 0           return $port;
543             }
544              
545             ##
546             # SBtool
547             #
548             sub get_sandbox_params {
549 0     0 0   my ($dir, $skip_strict) = @_;
550 0 0         confess "directory name required\n" unless $dir;
551 0 0         confess "directory $dir doesn't exist\n" unless -d $dir;
552 0 0         unless (is_a_sandbox($dir)) {
553 0 0         confess "directory <$dir> must be a sandbox\n" unless $skip_strict;
554             }
555 0           my %params = (
556             opt => undef,
557             conf => undef
558             );
559 0 0         if ( -f "$dir/$sandbox_options_file" ) {
560 0           $params{opt} = get_option_file_contents("$dir/$sandbox_options_file");
561             }
562             else {
563             # warn "options file $dir not found\n";
564 0           return;
565             }
566             # if ( -f "$dir/$sandbox_current_options" ) {
567             # $params{conf} =
568             # get_option_file_contents("$dir/$sandbox_current_options");
569             # }
570             # else {
571             # # warn "current conf file not found\n";
572             # return;
573             # }
574 0           return \%params;
575             }
576              
577             sub get_option_file_contents {
578 0     0 0   my ($file) = @_;
579 0 0         confess "file name required\n" unless $file;
580 0 0         confess "file $file doesn't exist\n" unless -f $file;
581 0           my %options;
582 0 0         open my $RFILE, q{<}, $file
583             or confess "can't open file $file\n";
584 0           while ( my $line = <$RFILE> ) {
585 0 0         next if $line =~ /^\s*$/;
586 0 0         next if $line =~ /^\s*#/;
587 0 0         next if $line =~ /^\s*\[/;
588 0           chomp $line;
589 0           my ( $key, $val ) = split /\s*=\s*/, $line;
590 0           $key =~ s/-/_/g;
591 0           $options{$key} = $val;
592             }
593 0           close $RFILE;
594             # print Dumper(\%options) ; exit;
595 0           return \%options;
596             }
597              
598             sub get_sb_info {
599 0     0 0   my ($search_path, $options) = @_;
600 0           my %ports = ();
601 0           my %all_info = ();
602 0           my $seen_dir = '';
603              
604             find(
605             {
606             no_chdir => 1,
607             follow => 1,
608             wanted => sub {
609 0 0   0     if ( $seen_dir eq $File::Find::dir ) {
610 0           return;
611             }
612 0           my $params;
613 0 0         if ( $params = get_sandbox_params($File::Find::dir, 1) ) {
614 0           $seen_dir = $File::Find::dir;
615 0           my $port = $params->{opt}{port};
616 0 0 0       if ( -f $params->{opt}{pid_file}
617             && -e $params->{opt}{socket} )
618             {
619 0           $ports{$port} = 1;
620 0 0         $all_info{$port} = $params if $options->{all_info};
621             }
622             else {
623 0 0         unless ( $options->{only_used} ) {
624 0           $ports{$port} = 0;
625 0 0         $all_info{$port} = $params if $options->{all_info};
626             }
627             }
628             }
629             }
630             },
631             $search_path || $options->{search_path}
632 0   0       );
633 0           return ( \%ports, \%all_info );
634             }
635              
636             sub is_a_sandbox {
637 0     0 0   my ($dir) = @_;
638 0 0         unless ($dir) {
639 0           confess "directory missing\n";
640             }
641 0           $dir =~ s{/$}{};
642 0           my %sandbox_files = map {s{.*/}{}; $_, 1 } glob("$dir/*");
  0            
  0            
643 0           my @required = (qw(data start stop send_kill clear use restart),
644             # $sandbox_current_options,
645             $sandbox_options_file );
646 0           for my $req (@required) {
647 0 0         unless (exists $sandbox_files{$req}) {
648 0           return;
649             }
650             }
651 0           return 1;
652             }
653              
654             sub is_sandbox_running {
655 0     0 0   my ($sandbox) = @_;
656 0 0         unless ( -d $sandbox ) {
657 0           confess "Can't see if it's running. <$sandbox> is not a sandbox\n";
658             }
659 0           my $sboptions = get_sandbox_params($sandbox);
660 0 0 0       unless ($sboptions->{opt}
      0        
661             && $sboptions->{opt}{'pid_file'}
662             && $sboptions->{opt}{'socket'}) {
663             # print Dumper($sboptions);
664 0           confess "<$sandbox> is not a single sandbox\n";
665             }
666 0 0 0       if ( ( -f $sboptions->{opt}{'pid_file'} )
667             && ( -e $sboptions->{opt}{'socket'}) ) {
668 0           return (1, $sboptions);
669             }
670             else {
671 0           return (0, $sboptions);
672             }
673             }
674              
675             sub get_ranges {
676 0     0 0   my ($options, $silent ) = @_;
677 0           my ( $ports, $all_info ) = get_sb_info(undef, $options);
678 0           my $minimum_port = $options->{min_range};
679 0           my $maximum_port = $options->{max_range};
680 0           my $range_size = $options->{range_size};
681 0 0         if ( $minimum_port >= $maximum_port ) {
682 0           croak "minimum range must be lower than the maximum range\n";
683             }
684 0 0         if ( ( $minimum_port + $range_size ) > $maximum_port ) {
685 0           croak "range too wide for given boundaries\n";
686             }
687 0           my $range_found = 0;
688             range_search:
689 0           while ( !$range_found ) {
690 0 0         if ( $minimum_port >= $maximum_port ) {
691 0           croak "can't find a range of $range_size "
692             . "free ports between "
693             . "$options->{min_range} and $options->{max_range}\n";
694             }
695 0           for my $i ( $minimum_port .. $minimum_port + $range_size ) {
696 0 0 0       if ( exists $ports->{$i} or ( $i >= $maximum_port ) ) {
697 0           $minimum_port = $i + 1;
698 0           next range_search;
699             }
700             }
701 0           $range_found = 1;
702             }
703 0 0         unless ($silent) {
704 0           printf "%5d - %5d\n", $minimum_port , $minimum_port + $range_size;
705             }
706 0           return $minimum_port;
707             }
708              
709             sub get_ports {
710 0     0 0   my ($options) = @_;
711 0           my ( $ports, $all_info ) = get_sb_info(undef, $options);
712              
713 0 0         if ( $options->{format} eq 'perl' ) {
    0          
714 0           print Data::Dumper->Dump( [$ports], ['ports'] );
715             print Data::Dumper->Dump( [$all_info], ['all_info'] )
716 0 0         if $options->{all_info};
717             }
718             elsif ( $options->{format} eq 'text' ) {
719 0           for my $port ( sort { $a <=> $b } keys %$ports ) {
  0            
720 0           printf "%5d %2d\n", $port, $ports->{$port};
721             }
722             }
723             else {
724 0           croak "unrecognized format -> $options->{format}\n";
725             }
726 0           return ( $ports, $all_info );
727             }
728              
729             sub exists_in_path {
730 0     0 0   my ($cmd) = @_;
731 0           my @path_directories = split /:/, $ENV{PATH}; ## no critic
732 0           for my $dir (@path_directories) {
733 0 0         if ( -x "$dir/$cmd") {
734 0           return "$dir/$cmd";
735             }
736             }
737 0           return ;
738             }
739              
740             sub runs_as_root {
741 0 0 0 0 0   if ( ($REAL_USER_ID == 0) or ($EFFECTIVE_USER_ID == 0)) {
742 0 0         unless ($ENV{SANDBOX_AS_ROOT}) {
743 0           die "MySQL Sandbox should not run as root\n"
744             . "\n"
745             . "If you know what you are doing and want to\n "
746             . "run as root nonetheless, please set the environment\n"
747             . "variable 'SANDBOX_AS_ROOT' to a nonzero value\n";
748             }
749             }
750             }
751              
752             sub mylogin_cnf_exists {
753 0     0 0   my $mylogin_cnf = "$ENV{HOME}/.mylogin.cnf";
754 0 0         if ( -r $mylogin_cnf) {
755 0 0         unless ($ENV{IGNORE_MYLOGIN_CNF}) {
756 0           die "MySQL Sandbox does not work with \$HOME/.mylogin.cnf,\n"
757             . "which is a file created by mysql_config_editor.\n"
758             . "Either remove the file or make it not readable by the current user.\n"
759             . "If you know what you are doing, you can skip this check by\n"
760             . "setting the variable IGNORE_MYLOGIN_CNF to a nonzero value.\n"
761             . "Be aware that having \$HOME/.mylogin.cnf can disrupt MySQL-Sandbox.\n"
762             . "Use it at your own risk.\n"
763             }
764             }
765             }
766              
767             #
768             # Replaces a path portion with an environment variable name
769             # if a match is found
770             #
771             sub use_env{
772 0     0 0   my ($path) = @_;
773 0           my @vars = (
774             'HOME',
775             'SANDBOX_HOME',
776             );
777 0 0         return '' unless $path;
778 0           for my $var (@vars) {
779 0 0         if ($path =~ /^$ENV{$var}/) {
780 0           $path =~ s/$ENV{$var}/\$$var/;
781 0           return $path;
782             }
783             }
784 0           return $path;
785             }
786              
787             sub sbinstr {
788 0     0 0   my ($msg) = @_;
789 0 0         unless ($ENV{SBINSTR}) {
790 0           return;
791             }
792 0           my $pname = $PROGRAM_NAME;
793 0 0         unless ($DEBUG) {
794 0           $pname =~ s{.*/}{};
795             }
796             open my $FH, '>>', $ENV{SBINSTR}
797 0 0         or die "can't write to $ENV{SBINSTR} ($!)\n";
798 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
799 0           $mon++;
800 0           $year +=1900;
801 0           print $FH "[$pname] - ",
802             sprintf('%4d-%02d%02d %02d:%02d:%02d',
803             $year, $mon, $mday, $hour, $min, $sec),
804             " - $msg \n";
805 0           close $FH;
806             }
807              
808             1;
809             __END__