Wednesday, November 25, 2009

Collaborative Sites

I recently found out about a cool software collaboration site: Stackoverflow. There one can get questions answered in a timely manner as well as earning "reputation" credits. Access is free and unlimited akin to a "wiki" site such as Wikipedia.

But what is best is there is an ecosystem of such site based on the same platform ( Stackexchange ):
... and probably much more!

Tuesday, November 17, 2009

Chrome Extension: mDNS Service Browser

As a stepping-stone towards making the browser my main portal to my applications, I have crafted a Google Chrome extension (based on an NPAPI plugin) that lists the services announced through Avahi / mDNS.
It is available here. Note that only Linux based systems are supported.



Wednesday, November 4, 2009

Notes on NPAPI based plugins

I am sharing the following diagram to (hopefully) lessen some pain from folks who intend to write NPAPI based plugins (Firefox, Chrome etc.).


STEP 1: the host browser loads the library corresponding to the MIME type expressed in the embed element tag.

STEP 2: the host browser calls the NP_Initialize function of the library and passes a reference to the public API of the browser (i.e. the NPNetscapeFuncs starting with NPN_ in the Mozilla documentation). The host browser also calls the NPP_New function in order to instantiate a "plugin instance".
STEP 3: the host browser calls the NP_GetValue function (with the variable NPPVpluginScriptableNPObject ) of the library. See step 4.
STEP 4: the plugin calls the NPN_CreateObject (through the reference provided in step 2) to instantiate a "scriptable NPObject". This object serves as proxy to the embed object declared on the source web page.
STEP 5: the web page can interact with the "scriptable object".




Saturday, October 17, 2009

Chrome Extension: Stackoverflow Tweaks

For those adept of Stackoverflow, the following extension provide tweaks to the web site. For version 0.1, the questions associated with ignored tags are rendered hidden.

The extension is available for installation through this link.

Thursday, October 15, 2009

Chrome Extension: Auto-Reload

IMPORTANT:  I've sold this extension. I no longer maintain it.

I've crafted an extension for Google Chrome: Auto-Reload. The extension enables automatic interval based page reloading.
This capability can be useful when developing web applications or just for the anxious folks waiting for a reply on a forum...
The extension can be installed by following this link (Chrome Gallery). Usage is simple: just press the "blue" button in the address bar to enable auto-reloading (the button will turn green when enabled). For more information, follow the extension's link to the gallery.

UPDATED: This extension is now hosted on the Google Chrome Gallery. Follow the above link.
UPDATED: The extension's project code home page is now located on GitHub at this link.
UPDATED:  The work currently on my plate for this extension can be viewed here.


UPDATED: The latest version information can be found on the extension's page here.


Latest Release: 7.6

  • Includes "per-URL" customization: uses the "context-menu" available through right-click

Release: 6.6

  • Sticky Mode : keep auto-reload status across page close
    • Allows a page configured as "Chrome Application" to auto-reload continuously
    • Use the "Create Application Shortcuts" menu item to configure 
  • Timeout & Randomize manual value editing (i.e. not only with "slider")
Suggestions Welcome!

Thursday, October 8, 2009

Erlang DBus Client Interface Library

When working on Linux, it is hard to ignore DBus for communicating with applications around the Desktop. Since I love Erlang so much, I wanted the capability to interface with DBus through Erlang scripts and/or applications hence I crafted an interface library.


The project in question is available at Erlang-DBus on GoogleCode. Note that the Erlang Port API library is also required: EPAPI.

Thursday, September 24, 2009

Mindmap of my projects

Here is a mindmap of my projects. I intend to keep this map up-to-date.

Thursday, September 17, 2009

ejabberd: changing the Erlang cookie for inter-node communications

I was recently playing around with ejabberd crafting myself a module in Erlang. I require the module to communicate with an external node through the RPC facility. Of course, Erlang mandates having the same cookie value throughout a node domain. To make matters worse, ejabberd does not appear to allow configuring the said cookie.

A solution to this problem: copy the desired .erlang.cookie file to /var/lib/erjabberd with the proper permissions (user: ejabberd, group: ejabberd, user-read-only).
Let me find the bug tracker for ejabberd now...

Monday, August 10, 2009

Daemons in Erlang

I've written Linux daemons in Python & C/C++ before and I must say that enough documentation is available online to make this experience as smooth as I believe it can be. Granted there are some interesting cases to take care of (e.g. zombies) but overall the procedure is fairly straightforward. Now my new found love in Erlang brings me once again to writing daemons.

First attempt
For my first go, I inspired myself with the procedure I had used with both Python & C/C++:
  • Use /var/run as a registry to hold the running state of the daemon
  • For starting the daemon, check /var/run for a running PID & if found, assume it is that of the daemon and abort
  • For stopping the daemon, check /var/run for a running PID & if not found, assume no daemon instance is running and thus spawn one
It turned out that to work OK but it seemed utterly complex and inflexible given that Erlang provides such excellent communication capabilities. On my next project, I had to try something else.
My second attempt
This time around, I relied on Erlang's intrinsic distributed programming capabilities to help manage the life-cycle of the daemon.
  1. A Python script serves as top-level manager ( it could very well be written in bash or Perl but I happen to like Python a lot and given its quasi ubiquity on Linux based distros, it is a safe operational choice ). The said script exports the two familiar management commands: start and stop.
  2. Start procedure: the manager script spawns a controller escript (Erlang Script) with for command-line parameter "status" . The return code ( exported through Erlang's erlang:halt/1 function ) is inspected by the manager script to determine if a daemon is already running. If not, the manager script spawns (with the option -detached) the actual Erlang based daemon.
  3. Stop procedure: the manager script spawns a controller escript with for command-line parameter "stop". The said controller script uses Erlang's rpc module to query a potential running daemon for its process PID (the Erlang emulator system PID (retrievable through os:getpid/0) i.e. not the PID of the daemon running process in the Erlang emulator). If the said RPC call succeed, the controller script issues a system command "kill -9 PID".
The communication between the controller script and the actual daemon is done through RPC and requires that both ends register with EPMD (Erlang's Port Mapper Daemon). I used the "short naming" convention to achieve this (the -sname option for erl).

Example of controller script written in escript:



#!/usr/bin/env escript
%% -*- erlang -*-
%%! -sname etrx_control
%%
%% @author Jean-Lou Dupont
%%

code_ok() -> 0.
code_error() -> 1.
code_daemon_found() -> 2.
code_daemon_not_found() -> 3.
code_lib_not_found() -> 4.
code_node_not_found() -> 5.


err_lib() -> "erlang-transmission not found".
err_daemon() -> "daemon not found".
err_node() -> "transmission node not found".

msg_pid() -> "daemon found, pid: ".
msg_usage() -> "usage: etrx_control [-q] [status|stop]".
msg_kill() -> "stop command sent".


main(["-q", "stop"]) -> run(quiet, stop);
main(["-q", "status"]) -> run(quiet, status);
main(["stop"]) -> run(verbose, stop);
main(["status"]) -> run(verbose, status);

main([]) ->
msg(verbose, code_ok(), msg_usage()),
halt(code_ok());

main([_Cmd]) ->
msg(verbose, code_ok(), msg_usage()),
halt(code_error()).


run(Feedback, stop) ->

add_cwd(),

case getstatus() of
daemon_not_found ->
msg(Feedback, code_daemon_not_found(), err_daemon());

{pid, Pid} ->
os:cmd("kill -9 "++Pid),
msg(Feedback, code_ok(), msg_kill());

{error, lib_not_found} ->
msg(Feedback, code_lib_not_found(), err_lib());

{error, node_not_found} ->
msg(Feedback, code_node_not_found(), err_node())
end;


run(Feedback, status) ->

%%for development
add_cwd(),

case getstatus() of
daemon_not_found ->
msg(Feedback, code_daemon_found(), err_daemon());

{pid, Pid} ->
msg(Feedback, code_daemon_found(), msg_pid(), Pid);

{error, lib_not_found} ->
msg(Feedback, code_lib_not_found(), err_lib());

{error, node_not_found} ->
msg(Feedback, code_node_not_found(), err_node())

end.


add_cwd() ->
{ok,Cwd}=file:get_cwd(),
Cp=Cwd++"/ebin",
code:add_pathsa([Cp]).


getstatus() ->
try
Status=rpc(status),
case Status of
rpcerror ->
daemon_not_found;

{pid, Pid} ->
{pid, Pid}
end
catch
error:undef ->
{error, lib_not_found};

_X:_Y ->
{error, node_not_found}
end.


msg(Feedback, Code, Msg) ->
case Feedback of
verbose ->
io:format("etrx_control: ~s~n", [Msg]);
_ ->
ok
end,
halt(Code).

msg(Feedback, Code, Msg1, Msg2) ->
msg(Feedback, Code, Msg1++Msg2).


%%%%%%%%%%%%%%
%% RPC related
%%%%%%%%%%%%%%

rpc(Command) ->
case dorpc(Command) of
rpcerror ->
daemon_not_found;

Response ->
Response
end.



dorpc(Message) ->
Node=tools:make_node(transmission),

case rpc:call(Node, transmission_daemon, api, [Message], 2000) of
{badrpc, _Reason} ->
rpcerror;

Other ->
Other
end.

Friday, July 31, 2009

Social toolbox

I am extensively online for both my personal & professional lives. Over the years, I've come to integrate a number of tools for keeping organized whilst socializing. My main requirements are:
  • Capacity to organize feed items (e.g. categorize)
  • Capacity to publish (share) selected feed items
  • Capacity to subscribe to feeds
  • Capacity to view feed items in real-time (as much as possible)
  • Capacity to view feed items in one window
Following is a diagram of my current toolbox that meets these requirements:


Sites without an RSS/ATOM feed
Once in a while I encounter sites without an RSS/ATOM feed (mainly on "local sites" e.g. town news) . I use Yahoo! Pipes to build a feed in such cases.

What is Diigo?
Diigo is a cool service: it is a social bookmarking site sporting the added features of "text highlighting" and sticky notes.

What tools would you suggest I add to my arsenal?




Tuesday, June 30, 2009

Erlang Syntax Highlighter


/**
* Code Syntax Highlighter.
* Version 1.5.2
* Copyright (C) 2004-2008 Alex Gorbatchev
* http://www.dreamprojections.com/syntaxhighlighter/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/

/*
* Erlang syntax contributed by Jean-Lou Dupont http://www.jldupont.com/
* file: shBrushErlang.js
*/
dp.sh.Brushes.Erlang = function()
{
// According to: http://erlang.org/doc/reference_manual/introduction.html#1.5
var keywords = 'after and andalso band begin bnot bor bsl bsr bxor '+
'case catch cond div end fun if let not of or orelse '+
'query receive rem try when xor'+
// additional
' module export import define';

this.regexList = [
{ regex: new RegExp("[A-Z][A-Za-z0-9_]+", 'g'), css: 'vars' },
{ regex: new RegExp("\\%.+", 'gm'), css: 'comment' },
{ regex: new RegExp("\\?[A-Za-z0-9_]+", 'g'), css: 'preprocessor' },
{ regex: new RegExp("[a-z0-9_]+:[a-z0-9_]+", 'g'), css: 'mod_func' },

{ regex: new RegExp('"(?!")(?:\\.|\\\\\\"|[^\\""\\n\\r])*"', 'gm'), css: 'string' },
{ regex: new RegExp("'(?!')(?:\\.|(\\\\\\')|[^\\''\\n\\r])*'", 'gm'), css: 'string' },

{ regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' },
];

this.CssClass = 'dp-erl';
this.Style = '.dp-erl .vars { color: rgb(184,134,11); }' +
'.dp-erl .mod_func { color: #CC00FF; }';
};

dp.sh.Brushes.Erlang.prototype = new dp.sh.Highlighter();
dp.sh.Brushes.Erlang.Aliases = ['erl', 'erlang'];


Test:


%% Author: Jean-Lou Dupont
%% Created: 2009-06-19
%% Description: Phidget InterfaceKit driver
%%
%% MESSAGES GENERATED:
%% ===================
%% {phidgeterror, {{Serial, Code, String}, date(), time(), now() }}
%% {device, {{Serial,Type,State, Version,Name,Label}, date(), time(), now() }}
%% {din, {{Serial, Index, Value}, date(), time(), now() }}
%% {dout, {{Serial, Index, Value}, date(), time(), now() }}
%% {sout, {{Serial, Index, Value}, date(), time(), now() }}
%%
%%
%% SUBSCRIPTIONS:
%% ==============
-module(ifk).

%%
%% MACROS
%%
-define(DRV_IFK_DEBUG, "pem_drv_ifk_debug").
-define(DRV_IFK, "pem_drv_ifk").

-define(SUBS, [phidgetdevice]).

%%
%% Exported Functions
%%
-export([
start_link/0,
start_link/1,
stop/0
]).

-export([
loop/0,
loop_handler/1,
handle_phidgetdevice/2,
filter_device/4,
handle_ifk/3,
handle_active/2,
handle_active/4,
handle_inactive/2,
handle_inactive/4,
ifk_drv/2,
send_to_reflector/1,
handle_crashed_driver/1,
clean_driver/1
]).

%% =============
%% API Functions
%% =============
start_link() ->
start_link([]).

start_link(Args) ->

{debug, Debug}=base:kfind(debug, Args,false),
DrvPath = base:pole(Debug, true, false, ?DRV_IFK_DEBUG, ?DRV_IFK),
LD = [{driver_path, DrvPath}],
NArgs = lists:append(Args, LD),

base:ilog(?MODULE, "start_link: Args[~p]~n",[NArgs]),

Pid = spawn_link(?MODULE, loop, []),
register( ?MODULE, Pid ),

?MODULE ! {args, NArgs},
{ok, Pid}.

stop() ->
?MODULE ! stop.

%% =====================================================
%% MAIN LOOP
%% =====================================================
loop() ->
receive

%% Send the 'ready' signal
{args, Args} ->
put(args, Args),
{driver_path, DrvPath} = base:kfind(driver_path, Args),
put(driver_path, DrvPath),
switch:subscribe(?MODULE, ?SUBS);

%% Send the 'ready' signal
{switch, subscribed} ->
%%base:ilog(?MODULE, "subscribed~n",[]),
switch:publish(?MODULE, ready, self());


stop ->
base:ilog(?MODULE, "exiting~n", []),
exit(ok);


%%verify that it is an "InterfaceKit" device
{_From, phidgetdevice, {M, Ts}} ->
%%base:ilog(?MODULE,"received 'phidgetdevice'~n", []),
handle_phidgetdevice(M, Ts);

{driver, Serial, Port, Pid} ->
base:ilog(?MODULE,"received driver info, Serial[~p] Port[~p] Pid[~p]~n", [Serial, Port, Pid]),
put({port, Serial}, Port),
put({pid, Serial}, Pid),
put({serial, Port}, Serial);

{crashed, Port} ->
clean_driver(Port);

%%don't know what todo
Other ->
base:ilog(?MODULE,"received unknown msg: [~p]~n", [Other])

end,
?MODULE:loop().

clean_driver(undefined) ->
base:ilog(?MODULE, "clean_driver: received undefined", []);

clean_driver(Port) ->
Serial = get({serial, Port}),
base:ilog(?MODULE, "clean_driver: Serial[~p] Port[~p]~n", [Serial, Port]),
erase({port, Serial}),
erase({pid, Serial}),
erase({serial, Port}).




%% =====================
%% HANDLER
%% =====================
handle_phidgetdevice(Msg, Ts) ->
%error_logger:info_msg("~p: handle_phidgetdevice, Msg[~p]~n", [?MODULE, Msg]),
{Serial, Type, State} = Msg,
filter_device(Serial, Type, State, Ts).

% we just want the InterfaceKit devices!
filter_device(Serial, Type, State, Ts) ->
case Type of
"PhidgetInterfaceKit" ->
handle_ifk(Serial, State,Ts);

_ ->
notifk
end.

%% Spawn 1 driver for each InterfaceKit device in "active" state
%% and get rid of detached device(s)
handle_ifk(Serial, inactive, Ts) ->
handle_inactive(Serial, Ts),
ok;

handle_ifk(Serial, active, Ts) ->
%error_logger:info_msg("~p: handle_ifk: Serial[~p] active~n", [?MODULE, Serial]),
handle_active(Serial, Ts),
ok;

handle_ifk(Serial, State, _) ->
base:elog(?MODULE, "handle_ifk: Serial[~p] INVALID STATE[~p]~n", [Serial, State]),
ok.


%% Open the driver if not already done
handle_active(Serial, Ts) ->
Port = get({port, Serial}),
Pid = get({pid, Serial}),
handle_active(Serial, Port, Pid, Ts).

handle_active(Serial, _, undefined, Ts) ->
handle_active(Serial, undefined, invalid, Ts);

% not sure this one is required
handle_active(Serial, undefined, undefined, Ts) ->
handle_active(Serial, undefined, invalid, Ts);


% Not active... yet
handle_active(Serial, undefined, invalid, _Ts) ->
DriverPath = get(driver_path),
%%error_logger:info_msg("~p: handle_active: DriverPath[~p]~n", [?MODULE, DriverPath]),
Pid = spawn(?MODULE, ifk_drv, [DriverPath, Serial]),
base:ilog(?MODULE, "handle_active: Serial[~p] Pid[~p]~n", [Serial, Pid]),
ok;


% Is it really active?
handle_active(Serial, _Port, Pid, Ts) ->
Active = is_process_alive(Pid),
case Active of
true ->
ok;
false ->
% clean-up required!
erase({pid, Serial}),
erase({port, Serial}),
handle_active(Serial, undefined, undefined, Ts)
end.


%% Close the driver if still active
handle_inactive(Serial, Ts) ->
Port = get({port, Serial}),
Pid = get({pid, Serial}),
handle_inactive(Serial, Port, Pid, Ts),
ok.

%not even defined it seems... nothing much to do
handle_inactive(_Serial, undefined, _, _) ->
ok;

handle_inactive(_Serial, _, undefined, _) ->
ok;

handle_inactive(Serial, Port, Pid, _Ts) ->
Active = is_process_alive(Pid),
case Active of
true ->
erlang:port_close(Port),
erlang:exit(Pid, kill),
erase({port, Serial}),
erase({pid, Serial}),
ok;
false ->
ok
end,
ok.

ifk_drv(ExtPrg, Serial) ->
%%error_logger:info_msg("~p:ifk_drv: Serial[~p] Pid[~p]~n",[?MODULE, Serial, self()]),
process_flag(trap_exit, true),
Param = erlang:integer_to_list(Serial),
Port = open_port({spawn, ExtPrg++" "++Param}, [{packet, 2}, binary, exit_status]),
error_logger:info_msg("~p: ifk_drv: Serial[~p] Port[~p] Pid[~p]~n",[?MODULE, Serial, Port, self()]),
put({port, Serial}, Port),
put({serial, Port}, Serial),

% signal back some useful mapping
?MODULE ! {driver, Serial, Port, self()},
loop_handler(Port).


%% =====================
%% IFK DRIVER loop
%% =====================


loop_handler(Port) ->
receive

{Port, {exit_status, _}} ->
base:elog(?MODULE, "loop_handler: an ifk driver exited/could not load~n", []),
handle_crashed_driver(Port),
exit(crashed);


{Port, {data, Data}} ->
Decoded = binary_to_term(Data),
%%base:ilog(?MODULE, "loop_handler: decoded msg[~p]~n", [Decoded]),
send_to_reflector(Decoded);

Msg ->
base:ilog(?MODULE,"loop_handler: msg[~p]~n", [Msg])
end,
loop_handler(Port).


send_to_reflector(Decoded) ->
{Msgtype, Msg} = Decoded,
M = {Msg, {date(), time(), now()}},
switch:publish(?MODULE, Msgtype, M).


%% =======================
%% CRASHED DRIVER RECOVERY
%% =======================


handle_crashed_driver(Port) ->
error_logger:warning_msg("~p: handle_crashed_driver: Port[~p] Pid[~p]~n", [?MODULE, Port, self()]),
?MODULE ! {crashed, Port}.

Tuesday, June 9, 2009

EPAPI: Erlang Port driver API library

Some time ago, I decided to turn to Erlang for my pet project (more and this in a later post). Erlang is a very nice language, it features:
  • asynchronous messaging
  • soft real-time capabilities
  • built-in multi-threading (with O(1) scheduler)
  • fault-tolerance & high availability functions (OTP release)
  • much more...
My project requires interfacing to Phidgets devices (USB connected sensors & other interface) and no Erlang client libraries exist for those so I decided to roll my own (I'll share my library at a later time).

The first step in interfacing with the Phidgets from Erlang was to devise a Port Driver library which I am making available here. For those who wish to get a taste of the API, jump directly to the documentation.

Have fun and don't hesitate to comment/contribute!

Sunday, May 31, 2009

Chrome extension for Diigo

UPDATED: Chrome's Extension subsystem keeps evolving. The latest changes break my extension and so I'll wait until Chrome is more stable before attempting to reissue this one.

I love the speed and sleeknest of Google Chrome but one thing that disappointed me was the lack of extensions. Now the wait is over: there is support for extensions in the developper release of Chrome.

Diigo's bookmarklet is nice but having to press the bookmarklet button each time I visit a site isn't much fun so I decided to package an extension for it: the extension is available here.

For those of you who do not know Diigo, one feature that distinguishes it from the other social bookmarking sites (such as Delicious) is its highlighting feature.

Thursday, February 19, 2009

First contact with Microsoft Azure

I got my LiveID invitation code 2days ago so I decided to invest 15minutes evaluating Azure. I am already pretty familiar with Google AppEngine having developed my own Web site on it but since I am striving to be "technology agnostic", I needed to investigate Microsoft Azure PaaS.


Revision Control
My first shock was to learn that SVN integration does not exist with Microsoft Visual Studio. I wonder how Microsoft developers handle this critical activity. This process must be addressed but I do not know how, being a Microsoft .NET newbie.

Deployment
It appears as though the VS environment automates the creation of the CS file ( akin to a Java WAR file it seems ). But the kicker: one must go through the web portal to upload the file to the Azure fabric... a tedious process to say the least compared to the "one click" AppEngine deployment made possible through some simple scripting in Eclipse.

I'll surely follow the "Hello World" tutorial and hopefully get a better feel for Azure in the coming weeks.

Friday, February 13, 2009

Cloud Computing Mind Map

I invite anybody wishing to share their knowledge of the Cloud Computing market to edit (click the MindMeister image on the bottom righthand side of the map or here) the following wikimap:


Update: I'd like to thank Jim Dowson ( linkedin profile ) for his extended contribution!
Update 2: I would suggest collaborators complete their Skype profile when registering with MindMeister: this facilitates group discussion. Thanks!

Wednesday, February 11, 2009

Uploading Outlook .pst to GMAIL

I was digging through old backups just recently and I decided to make some .PST files more productive ( e.g. searchable ). I could have opted for Google Desktop ( I think ) to sift through those backups but I instead followed the GMAIL road since I am in the process of trying to convince my wife to move completly to GMAIL from Outlook.


  • Step 1: install readpst ( link ) on a Linux machine ( I use Ubuntu primarily )
apt-get install readpst
  • Step 2: extract mail from the .pst file into mbox format
mkdir /tmp/mail
readpst "some-pst-file" -o /tmp/mail
  • Step 3: Configure Mozilla Thunderbird with GMAIL through IMAP: instructions can be found here.
  • Step 4: Install an mbox import/export extension for Thunderbird: available here.
  • Step 4: Import mbox folder hierarchy in Thunderbird: use Tools -> Import/Export in mbox/eml format -> Import mbox file menu option and select the appropriate dialog option ( I used #4 i.e. Select a directory where searching the mbox files to import (also in subdirectories ) ).
  • Step 5: Move the emails as desired.
Warnings
  • It appears that the use of the IMAP interface through Thunderbird might be cause some "silent failures" from time to time. I suggest moving/copying emails by small batch in order to track & manage this shortcoming.
  • Also, I would seem that GMAIL's web-interface is not able to track "in real-time" the changes and of course this problem is compounded by the amount of moving/copying being done in a particular time interval.
Have fun :-)


Thursday, January 29, 2009

DNS-323 hacking: Adding Telnet support

I bought myself a DLINK DNS-323 (hardware revision B1) NAS unit. I fitted it with 2 Western Digital WD10EADS 1TB "Green Caviar" hard drives.

I was a bit disappointed that the "independant disk" operation couldn't be used so I had to revert to a RAID-1 configuration.
Upgrading the firmware to 1.06 was a breeze ( don't forget to unzip the distribution file before attempting the upgrade :-)

Telnet
Hacking the device for Telnet support was a bit more problematic: the good source of information I could find was here. The package release 0.5 works like a charm.

Details
  • DNS-323 HW rev B1
  • 2x WD10EADS hard-disks
  • Firmware rev 1.06
  • Fun_Plug release 0.5

Monday, January 12, 2009

My Python library: command-line backup tools mainly

Through my daytime work, I use online tools a lot ( e.g. Gliffy, MindMeister )  so one could say I am confident about conducting work online.  Sometimes though I feel I need a safety net and this is why I like to use my moonlight time to calm my qualms about the possibility of loosing my work.


To this effect, I have created a python library with several command-line tools ( cross-platform i.e. Windows and Linux, tested on Ubuntu ) to perform, amongst other tasks, backup of my online documents.  The library documentation page is available here.