199 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
		
		
			
		
	
	
			199 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| 
								 | 
							
								#!/usr/bin/perl -w
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
							 | 
						||
| 
								 | 
							
								    if 0; # not running under some shell
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# MogileFS storage node daemon
							 | 
						||
| 
								 | 
							
								#  (perlbal front-end)
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# (c) 2004, Brad Fitzpatrick, <brad@danga.com>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use strict;
							 | 
						||
| 
								 | 
							
								use lib 'lib';
							 | 
						||
| 
								 | 
							
								use Perlbal;
							 | 
						||
| 
								 | 
							
								use Linux::AIO '1.3';
							 | 
						||
| 
								 | 
							
								use IO::Socket::INET;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								my $opt_daemonize;
							 | 
						||
| 
								 | 
							
								my $opt_config;
							 | 
						||
| 
								 | 
							
								exit 1 unless
							 | 
						||
| 
								 | 
							
								    Getopt::Long::GetOptions(
							 | 
						||
| 
								 | 
							
								               'daemon' => \$opt_daemonize,
							 | 
						||
| 
								 | 
							
								               'config=s' => \$opt_config,
							 | 
						||
| 
								 | 
							
								               );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								my $default_config = "/etc/mogilefs/mogstored.conf";
							 | 
						||
| 
								 | 
							
								$opt_config = $default_config if ! $opt_config && -e $default_config;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								my $conf;
							 | 
						||
| 
								 | 
							
								if (-e $opt_config) {
							 | 
						||
| 
								 | 
							
								    if (open F, $opt_config) {
							 | 
						||
| 
								 | 
							
								        local $/ = undef;
							 | 
						||
| 
								 | 
							
								        $conf = <F>;
							 | 
						||
| 
								 | 
							
								        close F;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        die "Couldn't open config file ($opt_config): $!\n";
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								my $out = sub { print STDOUT join("\n", map { ref $_ eq 'ARRAY' ? @$_ : $_ } @_) . "\n"; };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								my $path = "/var/mogdata";
							 | 
						||
| 
								 | 
							
								my $listen = "0.0.0.0:7500";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								$conf ||= "
							 | 
						||
| 
								 | 
							
								SERVER max_connections = 10000
							 | 
						||
| 
								 | 
							
								CREATE SERVICE mogstored
							 | 
						||
| 
								 | 
							
								SET mogstored.role = web_server
							 | 
						||
| 
								 | 
							
								SET mogstored.listen = $listen
							 | 
						||
| 
								 | 
							
								SET mogstored.docroot = $path
							 | 
						||
| 
								 | 
							
								SET mogstored.dirindexing = 0
							 | 
						||
| 
								 | 
							
								SET mogstored.enable_put = 1
							 | 
						||
| 
								 | 
							
								SET mogstored.enable_delete = 1
							 | 
						||
| 
								 | 
							
								SET mogstored.min_put_directory = 1
							 | 
						||
| 
								 | 
							
								SET mogstored.persist_client = 1
							 | 
						||
| 
								 | 
							
								ENABLE mogstored
							 | 
						||
| 
								 | 
							
								";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Perlbal::run_manage_command($_, $out) foreach split(/\n/, $conf);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								unless (Perlbal::Socket->WatchedSockets() > 0) {
							 | 
						||
| 
								 | 
							
								    die "Invalid configuration.  (shouldn't happen?)  Stopping.\n";
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if ($opt_daemonize) {
							 | 
						||
| 
								 | 
							
								    Perlbal::daemonize();
							 | 
						||
| 
								 | 
							
								} else {
							 | 
						||
| 
								 | 
							
								    print "Running.\n";
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# register our disk usage callback to get disk usage and keep an eye on how we're doing
							 | 
						||
| 
								 | 
							
								Perlbal::Socket::register_callback(1, sub {
							 | 
						||
| 
								 | 
							
								    my $err = sub { Perlbal::log('crit', $_[0]); return 60; };
							 | 
						||
| 
								 | 
							
								    my $rval = `df -l`;
							 | 
						||
| 
								 | 
							
								    foreach my $l (split /\r?\n/, $rval) {
							 | 
						||
| 
								 | 
							
								        next unless $l =~ /^(.+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)\s+(.+)$/;
							 | 
						||
| 
								 | 
							
								        my ($dev, $total, $used, $avail, $use, $disk) = ($1, $2, $3, $4, $5, $6);
							 | 
						||
| 
								 | 
							
								        next unless $disk =~ /^$path/;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # create string to print
							 | 
						||
| 
								 | 
							
								        my $now = time;
							 | 
						||
| 
								 | 
							
								        my $output = "time: $now\ndevice: $dev\ntotal: $total\nused: $used\navailable: $avail\nuse: $use\ndisk: $disk\n";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # open a file on that disk location called 'usage'
							 | 
						||
| 
								 | 
							
								        my $rv = open(FILE, ">$disk/usage");
							 | 
						||
| 
								 | 
							
								        unless ($rv) {
							 | 
						||
| 
								 | 
							
								            $err->("Unable to open '$disk/usage' for writing: $!");
							 | 
						||
| 
								 | 
							
								            next;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        print FILE $output;
							 | 
						||
| 
								 | 
							
								        close FILE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return 60;
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# setup a new socket for handling size requests
							 | 
						||
| 
								 | 
							
								my $server = IO::Socket::INET->new(LocalPort => 7501, # FIXME make this configurable
							 | 
						||
| 
								 | 
							
								                                   Type      => SOCK_STREAM,
							 | 
						||
| 
								 | 
							
								                                   Proto     => 'tcp',
							 | 
						||
| 
								 | 
							
								                                   Blocking  => 0,
							 | 
						||
| 
								 | 
							
								                                   Reuse     => 1,
							 | 
						||
| 
								 | 
							
								                                   Listen    => 10 )
							 | 
						||
| 
								 | 
							
								    or die "Error creating socket: $@\n";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# in Perl 5.6, we weren't always seeing this turned off by IO::Socket
							 | 
						||
| 
								 | 
							
								# so we have to do it manually here just to be sure.
							 | 
						||
| 
								 | 
							
								IO::Handle::blocking($server, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# accept handler for new workers
							 | 
						||
| 
								 | 
							
								my $accept_handler = sub {
							 | 
						||
| 
								 | 
							
								    my $csock = $server->accept()
							 | 
						||
| 
								 | 
							
								        or return;
							 | 
						||
| 
								 | 
							
								    IO::Handle::blocking($csock, 0);
							 | 
						||
| 
								 | 
							
								    my $client = SideChannelClient->new($csock);
							 | 
						||
| 
								 | 
							
								    $client->watch_read(1);
							 | 
						||
| 
								 | 
							
								    # ... that's it.  Client->new adds to event loop.
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# add to fd list so this one gets processed
							 | 
						||
| 
								 | 
							
								Perlbal::Socket->AddOtherFds(fileno($server) => $accept_handler);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# now start the main loop
							 | 
						||
| 
								 | 
							
								Perlbal::run();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#############################################################################
							 | 
						||
| 
								 | 
							
								### simple package for handling the stream request port
							 | 
						||
| 
								 | 
							
								package SideChannelClient;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use base qw{Perlbal::Socket};
							 | 
						||
| 
								 | 
							
								use fields qw{count};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# needed since we're pretending to be a Perlbal::Socket... never idle out
							 | 
						||
| 
								 | 
							
								sub max_idle_time { return 0; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								sub new {
							 | 
						||
| 
								 | 
							
								    my SideChannelClient $self = shift;
							 | 
						||
| 
								 | 
							
								    $self = fields::new($self) unless ref $self;
							 | 
						||
| 
								 | 
							
								    $self->SUPER::new(@_);
							 | 
						||
| 
								 | 
							
								    $self->state('side_channel');
							 | 
						||
| 
								 | 
							
								    $self->{count} = 0;
							 | 
						||
| 
								 | 
							
								    return $self;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								sub event_read {
							 | 
						||
| 
								 | 
							
								    my SideChannelClient $self = shift;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    my $bref = $self->read(1024);
							 | 
						||
| 
								 | 
							
								    return $self->close() unless defined $bref;
							 | 
						||
| 
								 | 
							
								    $self->{read_buf} .= $$bref;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while ($self->{read_buf} =~ s/^(.+?)\r?\n//) {
							 | 
						||
| 
								 | 
							
								        my $cmd = $1;
							 | 
						||
| 
								 | 
							
								        if ($cmd =~ /^size (\S+)$/) {
							 | 
						||
| 
								 | 
							
								            # increase our count
							 | 
						||
| 
								 | 
							
								            $self->{count}++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # validate uri
							 | 
						||
| 
								 | 
							
								            my $uri = $1;
							 | 
						||
| 
								 | 
							
								            if ($uri =~ /\.\./) {
							 | 
						||
| 
								 | 
							
								                $self->write("ERROR: uri invalid (contains ..)\r\n");
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # now stat the file to get the size and such
							 | 
						||
| 
								 | 
							
								            Linux::AIO::aio_stat("$path$uri", sub {
							 | 
						||
| 
								 | 
							
								                return if $self->{closed};
							 | 
						||
| 
								 | 
							
								                my $size = -e _ ? -s _ : -1;
							 | 
						||
| 
								 | 
							
								                $self->write("$uri $size\r\n");
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            # we don't understand this so pass it on to manage command interface
							 | 
						||
| 
								 | 
							
								            my @out;
							 | 
						||
| 
								 | 
							
								            Perlbal::run_manage_command($cmd, sub { push @out, $_[0]; });
							 | 
						||
| 
								 | 
							
								            $self->write(join("\r\n", @out) . "\r\n");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# override Danga::Socket's event handlers which die
							 | 
						||
| 
								 | 
							
								sub event_err { $_[0]->close; }
							 | 
						||
| 
								 | 
							
								sub event_hup { $_[0]->close; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# as_string handler
							 | 
						||
| 
								 | 
							
								sub as_string {
							 | 
						||
| 
								 | 
							
								    my SideChannelClient $self = shift;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    my $ret = $self->SUPER::as_string;
							 | 
						||
| 
								 | 
							
								    $ret .= "; size_requests=$self->{count}";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return $ret;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Local Variables:
							 | 
						||
| 
								 | 
							
								# mode: perl
							 | 
						||
| 
								 | 
							
								# c-basic-indent: 4
							 | 
						||
| 
								 | 
							
								# indent-tabs-mode: nil
							 | 
						||
| 
								 | 
							
								# End:
							 |