root/lib/Resmon/Status.pm

Revision e5d09e81c6b6816e904b241f852e542e067c2d4c, 5.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

shm status support

git-svn-id: https://labs.omniti.com/resmon/trunk@26 8c0face9-b7db-6ec6-c4b3-d5f7145c7d55

  • Property mode set to 100644
Line 
1 package Resmon::Status;
2
3 use strict;
4 use POSIX qw/:sys_wait_h/;
5 use IO::Handle;
6 use IO::File;
7 use IO::Socket;
8 use Socket;
9 use Fcntl qw/:flock/;
10 use IPC::SysV qw /IPC_CREAT IPC_RMID ftok S_IRWXU S_IRWXG S_IRWXO/;
11 use Data::Dumper;
12
13 my $SEGSIZE = 1024*256;
14 my $statusfile;
15 sub new {
16   my $class = shift;
17   my $file = shift;
18   return $statusfile if($statusfile);
19   return bless {
20     file => $file
21   }, $class;
22 }
23 sub get_shared_state {
24   my $self = shift;
25   my $blob;
26   my $len;
27   return unless($self->{shared_state});
28   # Lock shared segment
29   # Read in
30   shmread($self->{shared_state}, $len, 0, length(pack('i', 0)));
31   $len = unpack('i', $len);
32   shmread($self->{shared_state}, $blob, length(pack('i', 0)), $len);
33   # unlock
34   die "LEN: $len [$blob]\n";
35   $self->{store} = eval $blob;
36   return $self->{store};
37 }
38 sub store_shared_state {
39   my $self = shift;
40   return unless($self->{shared_state});
41   my $blob = Dumper($self->{store});
42
43   # Lock shared segment
44   # Write state and flush
45   shmwrite($self->{shared_state}, pack("l", length($blob)),
46            0, length(pack('i', 0))) || die "$!";
47   shmwrite($self->{shared_state}, $blob, length(pack('i', 0)),
48            length($blob) - length(pack('i', 0))) || die "$!";
49   # unlock
50 }
51 sub service {
52   my $self = shift;
53   my ($client, $req, $proto) = @_;
54   my $state = $self->get_shared_state();
55   if($req eq '/' or $req eq '/status' or $req eq '/status.txt') {
56     my $response = Dumper($self->{store});
57     print $client http_header(200, length($response)) if($proto);
58     print $client $response . "\r\n";
59   }
60 }
61 sub http_header {
62   my $code = shift;
63   my $len = shift;
64   return qq^HTTP/1.0 $code OK
65 Server: resmon
66 ^ . (defined($len) ? "Content-length: $len" : "Connection: close") . q^
67 Content-Type: text/plain
68
69 ^;
70 }
71 sub serve_http_on {
72   my $self = shift;
73   my $ip = shift;
74   my $port = shift;
75   $ip = INADDR_ANY if(!defined($ip) || $ip eq '' || $ip eq '*');
76   $port ||= 81;
77
78   my $handle = IO::Socket->new();
79   socket($handle, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
80     || die "socket: $!";
81   setsockopt($handle, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))
82     || die "setsockopt: $!";
83   bind($handle, sockaddr_in($port, $ip))
84     || die "bind: $!";
85   listen($handle,SOMAXCONN);
86
87   $self->{http_port} = $port;
88   $self->{http_ip} = $ip;
89
90   $self->{child} = fork();
91   if($self->{child} == 0) {
92     eval {
93       while(my $client = $handle->accept) {
94         my $req;
95         my $proto;
96         while(<$client>) {
97           eval {
98             s/\r\n/\n/g;
99             chomp;
100 print "CLIENT <= $_\n";
101             if(!$req) {
102               if(/^GET \s*(\S+) \s*HTTP\/(0\.9|1\.0|1\.1)\s*$/) {
103                 $req = $1;
104                 $proto = $2;
105               }
106               elsif(/^GET \s*(\S+)\s*$/) {
107                 $req = $1;
108                 $proto = undef;
109               }
110               else {
111                 die "protocol deviations.\n";
112               }
113             }
114             elsif(/^$/) {
115               $self->service($client, $req, $proto);
116               $req = undef;
117               $proto = undef;
118             }
119             elsif(/^\S+\s*:\s*.{1,4096}$/) {
120               # Valid request header... noop
121             }
122             else {
123               die "protocol deviations.\n";
124             }
125           };
126           if($@) {
127             print $client http_header(500);
128             print $client "$@";
129             last;
130           }
131         }
132         $client->close();
133       }
134     };
135     if($@) {
136       print STDERR "Error in listener: $@\n";
137     }
138     exit(0);
139   }
140   close($handle);
141   return;
142 }
143 sub open {
144   my $self = shift;
145   return 0 unless(ref $self);
146   return 1 if($self->{handle});  # Alread open
147   if($self->{file} eq '-' || !defined($self->{file})) {
148     $self->{handle} = IO::File->new_from_fd(fileno(STDOUT), "w");
149     return 1;
150   }
151   $self->{handle} = IO::File->new("> $self->{file}.swap");
152   die "open $self->{file}.swap failed: $!\n" unless($self->{handle});
153   $self->{swap_on_close} = 1; # move this to a non .swap version on close
154   chmod 0644, "$statusfile.swap";
155
156   unless($self->{shared_state}) {
157     my $id = ftok(__FILE__,$self->{http_port});
158     $self->{shared_state} = shmget($id, $SEGSIZE,
159                                    IPC_CREAT|S_IRWXU|S_IRWXG|S_IRWXO)
160       || die "$0: $!";
161   }
162   return 1;
163 }
164 sub store {
165   my ($self, $type, $name, $state, $mess) = @_;
166   $self->{store}->{$type}->{$name} = {
167     last_update => time,
168     state => $state,
169     message => $mess
170   };
171   $self->store_shared_state();
172   if($self->{handle}) {
173     $self->{handle}->print("$name($type) :: $state($mess)\n");
174   } else {
175     print "$name($type) :: $state($mess)\n";
176   }
177 }
178 sub close {
179   my $self = shift;
180   $self->{handle}->close() if($self->{handle});
181   $self->{handle} = undef;
182   if($self->{swap_on_close}) {
183     link("$self->{file}.swap", $self->{file});
184     unlink("$self->{file}.swap");
185     delete($self->{swap_on_close});
186   }
187 }
188 sub DESTROY {
189   my $self = shift;
190   my $child = $self->{child};
191   if($child) {
192     kill 15, $child;
193     sleep 1;
194     kill 9, $child if(kill 0, $child);
195     waitpid(-1,WNOHANG);
196   }
197   if($self->{shared_state}) {
198     shmctl($self->{shared_state}, IPC_RMID, 0);
199   }
200 }
201 1;
Note: See TracBrowser for help on using the browser.