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...

Wednesday, 10 October 2007

ANT Censuses of the Internet Address Space

http://www.isi.edu/ant/address/

(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)

Tuesday, 9 October 2007

Windows - Speeding up filesystems

http://msdn2.microsoft.com/en-us/library/ms940846.aspx
Disabling Last Access Time Stamps

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

  • Create the following registry key on your run-time image:

    Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
    Name: NtfsDisableLastAccessUpdate
    Type: REG_DWORD
    Value: 1


http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/fsutil_behavior.mspx?mfr=true

Fsutil: behavior

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


Adobe Flex Builder for Linux Alpha

From lwn.net & http://weblog.infoworld.com/techwatch/archives/014192.html
Tech Watch reports on Adobe's release of Adobe Flex Builder for Linux. "Flex Builder is an IDE for building rich Internet applications that leverage Flex and Adobe's Flash Player. Flex Builder Linux Alpha offers most Flex Builder 3 features such as project creation, code coloring, code hints and Ajax bridge. The release requires Sun Java Runtime Environment 1.5.x and the Eclipse 3.3 platform. The alpha producte supports several Suse, Red Hat and Ubuntu Linux distributions and the Firefox browser. Plans call for expanding support to other browsers and Linux distributions, said Phil Costa, director of product management at Adobe."

New York Times - The Next Leap for Linux

http://www.nytimes.com/2007/10/04/technology/circuits/04basics.html?ex=1192161600&en=47d6bc2f2d559a0f&ei=5070&emc=eta1

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.


Linkers documented

http://www.airs.com/blog/archives/38

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.


5 Great (And Free) Games You’re Not Playing Now

Found on

http://gigaom.com/2007/10/07/5-great-and-free-games-youre-not-playing-now/

via http://del.icio.us/







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.

Monday, 8 October 2007

Server logging visualisation using Ruby and OpenGL

From http://www.fudgie.org/



View real-time data and statistics from any logfile on any server you have
SSH access to, in an intuitive and entertaining way.


Here is the code:

#!/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

tim's shared items

Add to Google Reader or Homepage