(See attached file: it.15.all.16-subnet_stats.3px-per-point.annotated.png)
based on http://www.xkcd.com/195/
(See attached file: map_of_the_internet.jpg)
Explaining how they did it:
(See attached file: address_space_2007_smaller.png)
All of the interesting technological, artistic or just plain fun subjects I'd investigate if I had an infinite number of lifetimes. In other words, a dumping ground...
(See attached file: it.15.all.16-subnet_stats.3px-per-point.annotated.png)
based on http://www.xkcd.com/195/
(See attached file: map_of_the_internet.jpg)
Explaining how they did it:
(See attached file: address_space_2007_smaller.png)
If you are using an NTFS file system, you can increase the performance of EWF by disabling the last access date/time stamps.
To disable Last Access timestamps
Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
Name: NtfsDisableLastAccessUpdate
Type: REG_DWORD
Value: 1
Queries the current settings for generating 8.3 character-length file names, allowing extended characters in 8.3 character-length file names on NTFS volumes, updating the last access timestamp on NTFS volumes, how often quota events are written to the system log, and the size of the master file table (MFT) Zone. Enables or disables the use of 8.3 character-length file names, allowing extended characters in 8.3 character-length file names on NTFS volumes, and updating the last access timestamp on NTFS volumes. Enables you to change how often quota events are written to the system log and to change the amount of disk space reserved for the MFT Zone.
http://www.diskeeper.com/defrag.asp
Until recently, major PC makers shied away from Linux. Now the industry is watching as Dell is selling two Linux-equipped desktop models ($549 and $870, including a monitor) and a $774 notebook PC. ( Hewlett-Packard offers Linux systems to businesses, and Lenovo , the Chinese company that bought I.B.M.'s PC division, sells Linux machines in China and says it will soon offer Linux-based computers in the United States.)
The Ubuntu version of Linux runs the Dell computers. Because Dell does not have to pay a licensing fee for the operating system, the computers are $80 cheaper than PCs with Windows Vista Home Premium or $50 cheaper than the stripped-down Vista Basic edition.
I've been working on and off on a new linker. To my surprise, I've discovered in talking about this that some people, even some computer programmers, are unfamiliar with the details of the linking process. I've decided to write some notes about linkers, with the goal of producing an essay similar to my existing one about the GNU configure and build system.
As I only have the time to write one thing a day, I'm going to do this on my blog over time, and gather the final essay together later. I believe that I may be up to five readers, and I hope y'all will accept this digression into stuff that matters. I will return to random philosophizing and minding other people's business soon enough.
A Personal Introduction
Who am I to write about linkers?
I wrote my first linker back in 1988, for the AMOS operating system which ran on Alpha Micro systems. (If you don't understand the following description, don't worry; all will be explained below). I used a single global database to register all symbols. Object files were checked into the database after they had been compiled. The link process mainly required identifying the object file holding the main function. Other objects files were pulled in by reference. I reverse engineered the object file format, which was undocumented but quite simple. The goal of all this was speed, and indeed this linker was much faster than the system one, mainly because of the speed of the database.
I wrote my second linker in 1993 and 1994. This linker was designed and prototyped by Steve Chamberlain while we both worked at Cygnus Support (later Cygnus Solutions, later part of Red Hat). This was a complete reimplementation of the BFD based linker which Steve had written a couple of years before. The primary target was a.out and COFF. Again the goal was speed, especially compared to the original BFD based linker. On SunOS 4 this linker was almost as fast as running the cat program on the input .o files.
The linker I am now working, called gold, on will be my third. It is exclusively an ELF linker. Once again, the goal is speed, in this case being faster than my second linker. That linker has been significantly slowed down over the years by adding support for ELF and for shared libraries. This support was patched in rather than being designed in. Future plans for the new linker include support for incremental linking–which is another way of increasing speed.
There is an obvious pattern here: everybody wants linkers to be faster. This is because the job which a linker does is uninteresting. The linker is a speed bump for a developer, a process which takes a relatively long time but adds no real value. So why do we have linkers at all? That brings us to our next topic.
A Technical Introduction
What does a linker do?
It's simple: a linker converts object files into executables and shared libraries. Let's look at what that means. For cases where a linker is used, the software development process consists of writing program code in some language: e.g., C or C++ or Fortran (but typically not Java, as Java normally works differently, using a loader rather than a linker). A compiler translates this program code, which is human readable text, into into another form of human readable text known as assembly code. Assembly code is a readable form of the machine language which the computer can execute directly. An assembler is used to turn this assembly code into an object file. For completeness, I'll note that some compilers include an assembler internally, and produce an object file directly. Either way, this is where things get interesting.
In the old days, when dinosaurs roamed the data centers, many programs were complete in themselves. In those days there was generally no compiler–people wrote directly in assembly code–and the assembler actually generated an executable file which the machine could execute directly. As languages liked Fortran and Cobol started to appear, people began to think in terms of libraries of subroutines, which meant that there had to be some way to run the assembler at two different times, and combine the output into a single executable file. This required the assembler to generate a different type of output, which became known as an object file (I have no idea where this name came from). And a new program was required to combine different object files together into a single executable. This new program became known as the linker (the source of this name should be obvious).
Linkers still do the same job today. In the decades that followed, one new feature has been added: shared libraries.
http://gigaom.com/2007/10/07/5-great-and-free-games-youre-not-playing-now/
Written by Jane Pinckard
Sunday, October 7, 2007 at 12:00 PM PT
The growth of casual gaming has spawned an unprecedented variety, and
quality, of free web-based games. And as the development quality gets ever
higher, it attracts more talent, bigger budgets, and more ambitious scopes.
Here are five terrific games from independent developers you might have
missed the first time around. All of these can be played in your browser,
with Flash. Go on, it's Sunday — waste a little time!
GrowCube: This and the other equally gorgeous puzzles on EyezMaze require
imagination, trial and error, and maybe even some note-taking. Patience
will be rewarded by lovely animations and transformations in the game
world. The unique gameplay requires you to think several steps ahead.
Tanks: It's simple, and addictive — shoot various types of missiles at the
enemy tank, judging angles and mass to score direct hits. The best part is
that it can be played multiplayer, for up to five players, or just play the
AI alone.
Warbears: Another puzzle game, with an utterly charming design and engaging
sense of humor. Aid the bears in their mission by choosing the order of
their actions — or watch as they hilariously meet failure.
Dai Pai Dong: Cooking Mama meets the classic Diner Dash — in Hong Kong! Try
to keep up with the picky customers' demands by cooking to order and
serving up beers. Warning: playing before lunch may make you hungry for
sauteed crab.
Sprout: The hand-drawn art sets this puzzle game apart, as does the
creatively told story of one lonely seedling far from where it sprouted. By
planting the seed and growing a new plant, with new properties, guide the
seedling home.
And one extra, Scrabulous on Facebook: It's simply Scrabble, online, with
friends, through Facebook, and that's simply it. And it is indeed fabulous
— as well as a great way to stay in noncommittal contact with friends. And
of course you already know about Desktop Tower Defense, right? Good.
#!/usr/bin/env ruby
# gl_tail.rb v0.01 - OpenGL visualization of your server traffic
# Copyright 2007 Erlend Simonsen
#
# Licensed under the GPLv2
#
# I know, horrible code and global variables, and what not. But, it works,
# and it's too fun to watch my servers traffic in real time to clean up
# everything before releasing it.
#
# Installation instructions (Ubuntu/Debian):
# sudo apt-get install libopengl-ruby rubygems
# sudo gem install -y net-ssh
#
# Installation instructions (Mac OS/X):
#
# sudo gem install -y ruby-opengl net-ssh
# (You might need option 2 or higher)
#
# Configuration:
# Modify $SERVERS, $BLOCKS & $PARSERS to your liking. Either include a
# :password for each of your servers, or make sure your ssh-keys are
# correctly set up.
#
# Running:
# ./gl_tail.rb
#
# Changelog:
# 06 Oct 2007 - v0.01 Initial release
# 07 Oct 2007 - v0.02 Postfix parser
# IIS parser (Tucker Sizemore )
#
# Further ideas:
# Get rid of GLUT.BitmapCharacter and use textured polygons instead
# Allow more indicators (pulsing color/size, cubes, teapots, etc)
# Clickable links
# Drag 'n drop organizing
# Hide/show blocks with keypresses
# Limit display to specific host
# Background IP lookups
# Geolocation on IPS
#
require 'rubygems'
require 'opengl'
if RUBY_PLATFORM == "i386-mswin32"
require 'glut_prev'
else
require 'glut'
end
#require 'cgi'
#require 'resolv'
require_gem 'net-ssh'
#################
### Configuration
###
ENV['__GL_SYNC_TO_VBLANK']="1"
$WINDOW_WIDTH = 1200
$WINDOW_HEIGHT = 760
# $MIN_BLOB_SIZE = 0.07
$MIN_BLOB_SIZE = 0.05
$MAX_BLOB_SIZE = 0.6
$SERVERS = [
# List of machines to log in to via SSH, and which files to tail for traffic data.
{:name => 'server1', :host => 'server1.example.com', :user => 'joeuser', :password => 'secret', :command => 'tail -f', :files => ['/var/log/apache/access_log'], :color => [0.6, 0.6, 1.0, 1.0], :parser => :apache },
{:name => 'server2', :host => 'login.mycoolsite.com', :user => 'otheruser', :password => 'othersecret', :port => 22222, :command => 'xtail', :files => ['/usr/local/www/apps/myapp/current/log/production.log'], :color => [0.1, 0.6, 0.6, 1.0], :parser => :rails },
{:name => 'mail', :host => 'mail.spamme.com', :user => 'otheruser', :password => 'othersecret', :command => 'tail -f', :files => ['/var/log/maillog'], :color => [0.8, 1.0, 0.0, 1.0], :parser => :postfix },
]
$BLOCKS = [
# Sections with different information to display on the screen
{ :name => 'info', :position => :left, :order => 0, :size => 10, :auto_clean => false, :show => :total },
{ :name => 'sites', :position => :left, :order => 1, :size => 10 },
{ :name => 'content', :position => :left, :order => 2, :size => 5, :show => :total, :color => [1.0, 0.8, 0.4, 1.0] },
{ :name => 'status', :position => :left, :order => 3, :size => 10, :color => [1.0, 0.8, 0.4, 1.0] },
{ :name => 'users', :position => :left, :order => 4, :size => 10 },
{ :name => 'smtp', :position => :left, :order => 5, :size => 5 },
{ :name => 'urls', :position => :right, :order => 0, :size => 15 },
{ :name => 'slow requests', :position => :right, :order => 1, :size => 5, :show => :average },
{ :name => 'referrers', :position => :right, :order => 2, :size => 10 },
{ :name => 'user agents', :position => :right, :order => 3, :size => 5, :color => [1.0, 1.0, 1.0, 1.0] },
{ :name => 'mail', :position => :right, :order => 4, :size => 5 },
]
$PARSERS = {
# Parser which handles access_logs in combined format from Apache
:apache => Proc.new { |server,line|
_, host, user, domain, date, url, status, size, referrer, useragent = /^([\d.]+) (\S+) (\S+) \[([^\]]+)\] \"(.+?)\" (\d+) ([\S]+) \"([^\"]+)\" \"([^\"]+)\"/.match(line).to_a
if host
method, url, http_version = url.split(" ")
url, parameters = url.split('?')
server.add_activity(:block => 'sites', :name => server.name, :size => size.to_i/1000000.0) # Size of activity based on size of request
server.add_activity(:block => 'urls', :name => url)
server.add_activity(:block => 'users', :name => host, :size => size.to_i/1000000.0)
server.add_activity(:block => 'referrers', :name => referrer) unless (referrer.include?(server.name) || referrer.include?(server.host))
server.add_activity(:block => 'user agents', :name => useragent, :type => 3)
if( url.include?('.gif') || url.include?('.jpg') || url.include?('.png') || url.include?('.ico'))
type = 'image'
elsif url.include?('.css')
type = 'css'
elsif url.include?('.js')
type = 'javascript'
elsif url.include?('.swf')
type = 'flash'
elsif( url.include?('.avi') || url.include?('.ogm') || url.include?('.flv') || url.include?('.mpg') )
type = 'movie'
elsif( url.include?('.mp3') || url.include?('.wav') || url.include?('.fla') || url.include?('.aac') || url.include?('.ogg'))
type = 'music'
else
type = 'page'
end
server.add_activity(:block => 'content', :name => type)
server.add_activity(:block => 'status', :name => status, :type => 3) # don't show a blob
# Events to pop up
server.add_event(:block => 'info', :name => "Logins", :message => "Login...", :update_stats => true, :color => [1.5, 1.0, 0.5, 1.0]) if method == "POST" && url.include?('login')
server.add_event(:block => 'info', :name => "Sales", :message => "$", :update_stats => true, :color => [1.5, 0.0, 0.0, 1.0]) if method == "POST" && url.include?('/checkout')
server.add_event(:block => 'info', :name => "Signups", :message => "New User...", :update_stats => true, :color => [1.0, 1.0, 1.0, 1.0]) if( method == "POST" && (url.include?('/signup') || url.include?('/users/create')))
end
},
:rails => Proc.new { |server,line|
#Completed in 0.02100 (47 reqs/sec) | Rendering: 0.01374 (65%) | DB: 0.00570 (27%) | 200 OK [http://example.com/whatever/whatever]
_, ms, url = /^Completed in ([\d.]+) .* \[([^\]]+)\]/.match(line).to_a
if url
_, host, url = /^http[s]?:\/\/([^\/]+)(.*)/.match(url).to_a
server.add_activity(:block => 'sites', :name => host, :size => ms.to_f) # Size of activity based on request time.
server.add_activity(:block => 'urls', :name => url, :size => ms.to_f)
server.add_activity(:block => 'slow requests', :name => url, :size => ms.to_f)
# Events to pop up
server.add_event(:block => 'info', :name => "Logins", :message => "Login...", :update_stats => true, :color => [0.5, 1.0, 0.5, 1.0]) if url.include?('/login')
server.add_event(:block => 'info', :name => "Sales", :message => "$", :update_stats => true, :color => [1.5, 0.0, 0.0, 1.0]) if url.include?('/checkout')
server.add_event(:block => 'info', :name => "Signups", :message => "New User...", :update_stats => true, :color => [1.0, 1.0, 1.0, 1.0]) if(url.include?('/signup') || url.include?('/users/create'))
elsif line.include?('Processing ')
#Processing TasksController#update_sheet_info (for 123.123.123.123 at 2007-10-05 22:34:33) [POST]
_, host = /^Processing .* \(for (\d+.\d+.\d+.\d+) at .*\).*$/.match(line).to_a
if host
server.add_activity(:block => 'users', :name => host)
end
elsif line.include?('Error (')
_, error, msg = /^([^ ]+Error) \((.*)\):/.match(line).to_a
if error
server.add_event(:block => 'info', :name => "Exceptions", :message => error, :update_stats => true, :color => [1.0, 0.0, 0.0, 1.0])
server.add_event(:block => 'info', :name => "Exceptions", :message => msg, :update_stats => false, :color => [1.0, 0.0, 0.0, 1.0])
end
end
},
:postfix => Proc.new { |server,line|
if line.include?(': connect from')
_, host, ip = /: connect from ([^\[]+)\[(\d+.\d+.\d+.\d+)\]/.match(line).to_a
if host
server.add_activity(:block => 'smtp', :name => host, :size => 0.03)
end
elsif line.include?(' from=<') _, from, size = /: from=<([^>]+)>, size=(\d+)/.match(line).to_a
if from
server.add_activity(:block => 'mail', :name => from, :size => size.to_f/100000.0)
end
elsif line.include?(' to=<') if line.include?('relay=local') # Incoming _, to, delay, status = /: to=<([^>]+)>, .*delay=([\d.]+).*status=([^ ]+)/.match(line).to_a
server.add_activity(:block => 'mail', :name => to, :size => delay.to_f/10.0, :type => 5, :color => [1.0, 0.0, 1.0, 1.0])
server.add_activity(:block => 'status', :name => 'received', :size => delay.to_f/10.0, :type => 3)
else
# Outgoing
_, to, relay_host, delay, status = /: to=<([^>]+)>, relay=([^\[,]+).*delay=([\d.]+).*status=([^ ]+)/.match(line).to_a
server.add_activity(:block => 'mail', :name => to, :size => delay.to_f/10.0)
server.add_activity(:block => 'smtp', :name => relay_host, :size => delay.to_f/10.0)
server.add_activity(:block => 'status', :name => status, :size => delay.to_f/10.0, :type => 3)
end
end
},
:iis => Proc.new { |server,line|
_, date, time,serverip, url, referrer, port, size, host, useragent, status = /^([\d-]+) ([\d:]+) ([\d.]+) (.+? .+?) (\S+) (.+?) (\S+) ([\d.]+) (.+?) (\d+) (.*)$/.match(line).to_a
if host
method, url, http_version = url.split(" ")
url, parameters = url.split('?')
server.add_activity(:block => 'sites', :name => server.name, :size => size.to_i/1000000.0) # Size of activity based on size of request
server.add_activity(:block => 'urls', :name => url)
server.add_activity(:block => 'users', :name => host, :size => size.to_i/1000000.0)
server.add_activity(:block => 'referrers', :name => referrer) unless (referrer.include?(server.name) || referrer.include?(server.host))
server.add_activity(:block => 'user agents', :name => useragent, :type => 3)
if( url.include?('.gif') || url.include?('.jpg') || url.include?('.png') || url.include?('.ico'))
type = 'image'
elsif url.include?('.css')
type = 'css'
elsif url.include?('.js')
type = 'javascript'
elsif url.include?('.swf')
type = 'flash'
elsif( url.include?('.avi') || url.include?('.ogm') || url.include?('.flv') || url.include?('.mpg') )
type = 'movie'
elsif( url.include?('.mp3') || url.include?('.wav') || url.include?('.fla') || url.include?('.aac') || url.include?('.ogg'))
type = 'music'
else
type = 'page'
end
server.add_activity(:block => 'content', :name => type)
server.add_activity(:block => 'status', :name => status, :type => 3) # don't show a blob
# Events to pop up
server.add_event(:block => 'info', :name => "Logins", :message => "Login...", :update_stats => true, :color => [1.5, 1.0, 0.5, 1.0]) if method == "POST" && url.include?('login')
server.add_event(:block => 'info', :name => "Sales", :message => "$", :update_stats => true, :color => [1.5, 0.0, 0.0, 1.0]) if method == "POST" && url.include?('/checkout')
server.add_event(:block => 'info', :name => "Signups", :message => "New User...", :update_stats => true, :color => [1.0, 1.0, 1.0, 1.0]) if( method == "POST" && (url.include?('/signup') || url.include?('/users/create')))
end
}
}
###
### Configuration end
######################
# Lots of hacks and bad code below. :-)
$BLOBS = { }
$FPS = 50.0
$ASPECT = 0.6
$TOP = 11.0
$RIGHT_COL = 11.0
$LEFT_COL = -20.0
$LINE_SIZE = 0.3
$BLOB_OFFSET = 7.0
$STATS = []
class Item
attr_accessor :message, :size, :color, :type
def initialize(message, size, color, type)
@message = message
@size = size
@color = color
@type = type
end
end
class Activity
attr_accessor :x, :y, :z, :type, :wx, :wy, :wz
def initialize(message, x,y,z, color, size, type=0)
@message = message
@x, @y, @z = x, y, z
# @xi, @yi, @zi = 0.18 + ( (rand(100)/100.0 - 0.5) * 0.02 ), (rand(100)/100.0 - 0.5) * 0.02, 0
@xi, @yi, @zi = 0.18 , 0.03, 0
if @x >= 0.0
@xi = -@xi
end
@xi = (rand(100)/100.0 * 0.02) - 0.01 if type == 2
@color = color
@size = size
@type = type
end
def render
if @type == 5
dy = @wy - @y
if dy.abs < y =" @wy" dx =" @wx" x =" @wx" x ="="" x =" 20.0" y =" -$TOP" yi =" -@yi" x =" 30.0" type ="="" type ="="" type ="="" list =" GL.GenLists(1)" type ="="" list =" GL.GenLists(1)" type ="="" list =" GL.GenLists(1)" type =" 0," right =" false," start_position =" -$TOP)" name =" name" right =" right" x =" (right" y =" start_position" z =" 0" wy =" start_position" color =" color" size =" 0.01" queue =" []" pending =" []" activities =" []" messages =" 0" rate =" 0" total =" 0" sum =" 0" average =" 0.0" last_time =" 0" step =" 0," updates =" 0" active =" false" type =" type" type ="="" average =" @sum" rate ="="" rate =" 1.0" messages =" 0" rate ="="" rate =" 1.0" messages =" 0" active =" true"> 0
@updates += 1
@rate = (@rate.to_f * 59 + @messages) / 60
@messages = 0
if @pending.size > 0
if @pending.size == 1
@step = rand(1000)
else
@step = 1.0 / (@queue.size + @pending.size) * 1000
end
@queue = @queue + @pending
@pending = []
else
@step = 0
end
@last_time = GLUT.Get(GLUT::ELAPSED_TIME)
@last_time -= @step unless @queue.size == 1
end
def render(options = { })
@x = (@right ? $RIGHT_COL : $LEFT_COL)
d = @wy - @y
if d.abs < y =" @wy"> 0 ? [10.0, 1.0, 1.0, 1.0] : @color ))
GL.Translate(@x, @y, @z)
GL.RasterPos(0.0, 0.0)
if @type == 0
if @rate < txt = " r/m " txt = "#{sprintf(" type ="="" total ="="" txt = " total " txt = "#{sprintf(" type ="="" average ="="" txt = " avg " txt = "#{sprintf(" list =" GL.GenLists(1)" list =" GL.GenLists(1)" t =" GLUT.Get(GLUT::ELAPSED_TIME)"> 0 && @last_time + @step < last_time =" t" item =" @queue.pop" url =" item.message" color =" item.color" size =" item.size" type =" item.type" size =" $MIN_BLOB_SIZE"> $MAX_BLOB_SIZE
size = $MAX_BLOB_SIZE
end
if type == 2
@activities.push Activity.new(url, 0.0 - (0.043 * url.length), $TOP, 0.0, color, size, type)
elsif type == 5
a = Activity.new(url, 0.0, $TOP, 0.0, color, size, type)
a.wx = @x
a.wy = @y + 0.05
@activities.push a
elsif type != 4
if @x >= 0
@activities.push Activity.new(url, @x, @y, @z, color, size, type)
else
@activities.push Activity.new(url, @x + $BLOB_OFFSET, @y, @z, color, size, type)
end
end
end
@activities.each do |a|
if a.x > 18.0 || a.x < -18.0 @activities.delete a else a.wy = @y + 0.05 if a.type == 5 a.render $STATS[1] += 1 end end end end class Block attr_reader :name, :position, :order, :bottom_position def initialize(options) @name = options[:name] @position = options[:position] || :left @size = options[:size] || 10 @clean = options[:auto_clean] || true @order = options[:order] || 100 @color = options[:color] @show = case options[:show] when :rate: 0 when :total: 1 when :average: 2 else 0 end @header = Element.new(@name.upcase , [1.0, 1.0, 1.0, 1.0], @show, @position == :right) @elements = { } @bottom_position = -$TOP end def render(num) return num if @elements.size == 0 @header.wy = $TOP - (num * $LINE_SIZE) @header.render num += 1 sorted = case @show when 0: @elements.values.sort { |k,v| v.rate <=> k.rate}[0..@size-1]
when 1: @elements.values.sort { |k,v| v.total <=> k.total}[0..@size-1]
when 2: @elements.values.sort { |k,v| v.average <=> k.average}[0..@size-1]
end
sorted.each do |e|
e.wy = $TOP - (num * $LINE_SIZE)
e.render
$STATS[0] += 1
if e.rate <= 0.0001 && e.active && e.updates > 4
@elements.delete(e.name)
end
num += 1
end
(@elements.values - sorted).each do |e|
$STATS[0] += 1
e.activities.each do |a|
a.render
if a.x > 18.0 || a.x < -18.0 e.activities.delete a end end if e.activities.size == 0 && @clean && e.updates > 4
@elements.delete(e.name)
end
end
@elements.delete_if { |k,v| (!sorted.include? v) && v.active && v.activities.size == 0} if @clean
@bottom_position = $TOP - ((sorted.size > 0 ? (num-1) : num) * $LINE_SIZE)
num + 1
end
def add_activity(options = { })
@elements[options[:name]] ||= Element.new(options[:name], @color || options[:color], @show, @position == :right, @bottom_position)
@elements[options[:name]].add_activity(options[:message], options[:size] || 0.01, options[:type] || 0 )
end
def add_event(options = { })
@elements[options[:name]] ||= Element.new(options[:name], options[:color], @show, @position == :right)
@elements[options[:name]].add_event(options[:message], options[:update_stats] || false)
end
def update
@elements.each_value do |e|
e.update
end
end
end
class Server
attr_reader :name, :host, :color, :parser
def initialize(options)
@name = options[:name] || options[:host]
@host = options[:host]
@color = options[:color] || [1.0, 1.0, 1.0, 1.0]
@parser = $PARSERS[options[:parser]] || $PARSERS[:apache]
@blocks = options[:blocks]
end
#block, message, size
def add_activity(options = { })
block = @blocks[options[:block]].add_activity( { :name => @name, :color => @color, :size => 0.03 }.update(options) )
end
#block, message
def add_event(options = { })
block = @blocks[options[:block]].add_event( { :name => @name, :color => @color, :size => 0.03}.update(options) )
end
end
class GlTail
def draw
GL.Clear(GL::COLOR_BUFFER_BIT);
# GL.Clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT);
GL.PushMatrix()
positions = Hash.new
$STATS = [0,0]
@blocks.values.sort { |k,v| k.order <=> v.order}.each do |block|
positions[block.position] = block.render( positions[block.position] || 0 )
end
GL.PopMatrix()
GLUT.SwapBuffers()
@frames = 0 if not defined? @frames
@t0 = 0 if not defined? @t0
@frames += 1
t = GLUT.Get(GLUT::ELAPSED_TIME)
if t - @t0 >= 5000
seconds = (t - @t0) / 1000.0
$FPS = @frames / seconds
printf("%d frames in %6.3f seconds = %6.3f FPS\n",
@frames, seconds, $FPS)
@t0, @frames = t, 0
puts "Elements[#{$STATS[0]}], Activities[#{$STATS[1]}]"
end
end
def idle
GLUT.PostRedisplay()
do_process
end
# Change view angle, exit upon ESC
def key(k, x, y)
case k
when 27 # Escape
exit
end
GLUT.PostRedisplay()
end
# Change view angle
def special(k, x, y)
GLUT.PostRedisplay()
end
# New window size or exposure
def reshape(width, height)
$ASPECT = height.to_f / width.to_f
puts "Reshape: #{width}x#{height} = #{$ASPECT}"
GL.Viewport(0, 0, width, height)
GL.MatrixMode(GL::PROJECTION)
GL.LoadIdentity()
GL.Frustum(-2.0, 2.0, -$ASPECT*2, $ASPECT*2, 5.0, 60.0)
$TOP = 19.0 * $ASPECT
$LINE_SIZE = 0.5 * (1122/height.to_f) * $ASPECT
$BLOB_OFFSET = 11.6 * (1122/height.to_f) * $ASPECT
$RIGHT_COL = 18.3 * $ASPECT
GL.MatrixMode(GL::MODELVIEW)
GL.LoadIdentity()
GL.Translate(0.0, 0.0, -50.0)
end
def init
GL.Lightfv(GL::LIGHT0, GL::POSITION, [5.0, 5.0, 10.0, 0.0])
GL.Disable(GL::CULL_FACE)
GL.Enable(GL::LIGHTING)
GL.Enable(GL::LIGHT0)
GL.Disable(GL::DEPTH_TEST)
GL.Disable(GL::NORMALIZE)
@channels = Array.new
@sessions = Array.new
@servers = Hash.new
@blocks = Hash.new
@mode = 0
$BLOCKS.each do |b|
@blocks[b[:name]] = Block.new b
end
$SERVERS.each do |s|
puts "Connecting to #{s[:host]}..."
session_options = { }
session_options[:port] = s[:port] if s[:port]
# session_options[:verbose] = :debug
if s[:password]
session = Net::SSH.start(s[:host], s[:user], s[:password], session_options)
else
session = Net::SSH.start(s[:host], s[:user], session_options)
end
do_tail session, s[:name], s[:color], s[:files].join(" "), s[:command]
session.connection.process
@sessions.push session
@servers[s[:name]] ||= Server.new(:name => s[:name] || s[:host], :host => s[:host], :color => s[:color], :parser => s[:parser], :blocks => @blocks )
end
@since = GLUT.Get(GLUT::ELAPSED_TIME)
end
def visible(vis)
GLUT.IdleFunc((vis == GLUT::VISIBLE ? method(:idle).to_proc : nil))
end
def mouse(button, state, x, y)
@mouse = state
@x0, @y0 = x, y
end
def motion(x, y)
if @mouse == GLUT::DOWN then
end
@x0, @y0 = x, y
end
def initialize
GLUT.Init()
GLUT.InitDisplayMode(GLUT::RGB | GLUT::DOUBLE)
GLUT.InitDisplayMode(GLUT::RGB | GLUT::DEPTH | GLUT::DOUBLE)
GLUT.InitWindowPosition(0, 0)
GLUT.InitWindowSize($WINDOW_WIDTH, $WINDOW_HEIGHT)
GLUT.CreateWindow('glTail')
init()
GLUT.DisplayFunc(method(:draw).to_proc)
GLUT.ReshapeFunc(method(:reshape).to_proc)
GLUT.KeyboardFunc(method(:key).to_proc)
GLUT.SpecialFunc(method(:special).to_proc)
GLUT.VisibilityFunc(method(:visible).to_proc)
GLUT.MouseFunc(method(:mouse).to_proc)
GLUT.MotionFunc(method(:motion).to_proc)
end
def start
GLUT.MainLoop()
end
def parse_line( ch, data )
ch[:buffer].gsub(/\r\n/,"\n").gsub(/\n/, "\n\n").each("") do |line|
unless line.include? "\n\n"
ch[:buffer] = "#{line}"
next
end
line.gsub!(/\n\n/, "\n")
line.gsub!(/\n\n/, "\n")
server = @servers.values.find { |v| (v.host == ch[:host]) && (v.name == ch[:name]) }
server.parser.call(server, line)
end
ch[:buffer] = "" if ch[:buffer].include? "\n"
end
def do_tail( session, name, color, file, command )
session.open_channel do |channel|
puts "Channel opened on #{session.host}...\n"
channel[:host] = session.host
channel[:name] = name
channel[:color] = color
channel[:buffer] = ""
channel.request_pty :want_reply => true
channel.on_data do |ch, data|
ch[:buffer] << active =" 0" active ="="">= 1000
@since = GLUT.Get(GLUT::ELAPSED_TIME)
@channels.each { |ch| ch.connection.ping! }
@blocks.each_value do |b|
b.update
end
end
self
end
end
GlTail.new.start