Monday, November 17, 2014

Opening DICOM files in the browser

Traditionally, there has been two main trends in the development of applications oriented to end users. The first one has been the most extended for many years: The development of native solutions, tied to a specific platform. The second one came into scene with the adoption of the web and its possibilities related to the creation of web based applications.

When HTML5 was introduced, the possible scenarios for web based applications broadened as never before. Web applications started to appear even in the most unexpected domains.

The medical imaging field did also benefit from this set of new tools and possibilities. Reading and rendering DICOM files was already possible in pre-HTML5 environments, but the new framework just made things much simpler and easier to implement, with no need of any kind of obscure hacks.

In my particular case, I decided, nearly two years ago, to switch from a flash (actionscript) based solution to display medical images to a different set of tools, using just plain HTML and javascript.

I started to work on my own image viewer, while following and getting some ideas from other products, like dwv.

Since this particular application was also oriented to run also on mobile devices, I tried to do my best trying to find an efficient solution. Some of the requirements of the new product were to minimize the amount of necessary memory as well as to maximize the performance of the final product.

One of the decisions to meet the performance goal was to rely on typed arrays, in particular DataView, since we had to read binary file headers containing heterogeneous data.
"Typed Arrays are a relatively recent addition to browsers, born out of the need to have an efficient way to handle binary data in WebGL..."
"DataView is the second type of view and it is meant for handling heterogeneous data. Instead of having an array-like API, the DataView object provides you a get/set API to read and write arbitrary data types at arbitrary byte offsets. DataView works great for reading and writing file headers and other such struct-like data."
Everything seemed perfect. The core language libraries offered us some tools which fit perfectly with the tasks we wanted to do. The fact of using the core tools also matches perfectly some well known principles of programming:

- Do not reinvent the wheel
- Center your efforts in the logic specific to your problem
- This is javascript: Native code has been compiled, while the code typed by anyone has to be interpreted.
- Core libraries in general include highly optimized code.

Sadly these premises ended being completely wrong in this case. All the coding efforts to get a fast code were unsuccessful. The implementation of the DataView class has some kind of weird problem which leads to horrible performance results.

This problem was noticed and reported longtime ago, but at the present day the problem still persists.

I have done some performance tests in a range of different machines. The code using DataView resulted to be extremely slow when compared with writing your own functions for raw data reading. Results are between 20x and 50x slower, depending on the test platform. Relying on DataView in your code is a nonsense given the current implementation problems. I do not catch the point of this class given such a bad results.

DataView performs between 20x and 50x slower than raw reading on a modern computer
So, the final thought, if you pretend to get performance on your javascript code, avoid using the  DataView javascript object. Otherwise you will end up loosing a big amount of your precious time writing useless code.

Wednesday, February 15, 2012

AirPrint in corporate networks (II)


After the configuration of AirPrint to use it on standard printers in small networks, the next step is to extend the AirPrint shared printers in such a way that the printers can be reached from client devices in different subnets. This is basically a rewrite of the original article, but giving additional information and details specific to Linux, in particular to the RHEL and derived such as CentOS and Scientific Linux.

In order to achieve that, we setup a nameserver which, in addition to the usual services, adds specific information related to AirPrint. If we do not have access to the corporate DNS or we prefer not to play with such a server to avoid messing it, we can create an additional nameserver just for the wireless devices (those connecting from the wireless network range).

A very common common scenario for wireless devices is to have a DHCP server to assign IP addresses to these devices. Our particular setup includes several access points which relay DHCP requests to a central DHCP server. This server gives not only IP addresses to wireless devices, but also some additional information such as the address of the modified DNS server as well as a default domain name. Wired devices usually do have fixed IP addresses and they can continue using the corporate DNS, which has no information or knowledge about anything related to AirPrint.

This second version was initially installed on a computer running CentOS 5.6, but a few weeks after that installation we had to migrate the system to a new hardware. In the new hardware, we installed a different version: ScientificLinux 6.1. This distribution is also based on RHEL, so we assume that the following tutorial will be valid for any distribution based on RHEL 6.x. This machine will be referred to as AirPrint gateway server in this tutorial.

If you want to use your Linux box to provide with all the necessary services, you can setup your own DHCP server. If you (your company) already have a DHCP running and do not want to modify it, just do make the necessary changes to take into account the wireless devices.

Our particular scenario (*) is the next one:
Wired computers: 10.36.11.0/24
AirPrint gateway server (DHCP, named, CUPS): 10.36.11.22
Main corporate DNS server: 10.36.11.21
Wireless computers: 10.41.137.192/26 (DHCP range: 202..254)
Domain name: intranet.mycorporation.com

(*) This is just a simplification of the real scenario where some names and values have been changed.

After making the necessary changes to fit your particular scenario, you should add something similar to that to your /etc/dhcpd.conf file:

subnet 10.41.137.192 netmask 255.255.255.192 {
    range 10.41.137.202 10.41.137.254;
    option domain-name "wifi.intranet.mycorporation.com";
    option domain-name-servers 10.36.11.22;
    option routers 10.41.137.193;
    option subnet-mask 255.255.255.192;
    option broadcast-address 10.41.137.255;
}

The goal is to make wireless devices point to a modified DNS instead of the main corporate DNS server as well as to define a default domain name. The new DNS will manage just the necessary information in order to make AirPrint printers reachable. Other additional DNS configurations will be managed by the original nameserver, and the new one will just redirect every DNS query (except those related to AirPrint devices) to the corporate nameserver.

So, the next step is to setup our DNS (bind / named) server on our Linux box. You can find detailed information on how to setup this service. Our particular server will just define the specific zone necessary to access AirPrint from wifi devices, while relying to the main corporate server any other DNS query. You can choose to install either a standard DNS server or a chrooted DNS server.

The location of the configuration directory may vary (/etc/named/named.conf, /var/named/chroot/etc/named.conf). The named.conf file will be modified to include a zone with the necessary definitions in order to allow the wireless devices to discover the printers.

options {
# Add this to your current options ...
    forwarders { 10.36.11.21 ; }; // The main corporate DNS
    forward first;
    allow-query-cache { any; }; // Could be limited to the wifi range
    auth-nxdomain no;
    recursion yes;
};

# Include the file with the zone definitions
zone "wifi.intranet.mycorporation.com" {
    type master;
    file "/var/named/slaves/wifi.intranet.mycorporation.com.zone";
    allow-update { none; };
};

Take into account that if your named installation is chrooted, the zone file will have to be placed under /var/named/chroot/

The zone definition file has two goals: The first one is to define the host acting as a print server (wifiprintserver in this sample). It also has been created a cups server with the definitions of all the printers wanted to be shared via AirPrint. The computer having the role of CUPS server is the same host acting as DHCP server and named server. According to this scenario, the beginning of the zone definition file will look like this:

# cat /var/named/slaves/wifi.intranet.mycorporation.com.zone

$ORIGIN wifi.intranet.mycorporation.com.
$TTL 23200
@   IN   SOA  ns1.wifi.intranet.mycorporation.com.  hostmaster.wifi.intranet.mycorporation.com. (
    2011051612 ; serial
    21600      ; refresh 6h
    3600       ; retry 1h
    604800     ; expire 1week
    23200      ; minimum TTL 12h
    )

    IN   NS   ns1.wifi.intranet.mycorporation.com.

ns1   IN   A   10.36.11.22  ; YOUR_AUXILIARY_DNS_SERVER_IP_ADDR

wifiprintserver IN A 10.36.11.22 ; YOURCUPSPRINTSERVER_IPADDR
lb._dns-sd._udp      IN PTR @
b._dns-sd._udp      IN PTR @

; ******************************

The DNS service can make use of multicast DNS (mDNS) and DNS Service Discovery (DNS-SD) to announce the AirPrint services. After the first section containing the definition of the print server node, it comes the specific area for every printer. The new DNS zone definition file will include entries with a content similar to that exported via avahi_dameon in the previous approach.

Every printer entry will look something like that:

_cups._sub._ipp._tcp     IN PTR corporateprinter01._printer._tcp
_universal._sub._ipp._tcp    IN PTR corporateprinter01._printer._tcp

corporateprinter01._printer._tcp    IN    SRV   0   0   631  wifiprintserver
corporateprinter01._printer._tcp    IN    TXT   (
    "txtvers=1"
    "qtotal=1"
    "rp=printers/corporateprinter01"
    "adminurl=http://wifiprintserver:631/printers/corporateprinter01"
    "ty=HP Laserjet 4050N"
    "product=(GPL Ghostscript)"
    "transparent=t"
    "copies=t"
    "duplex=f"
    "color=f"
    "pdl=application/octet-stream,application/pdf,application/postscript,image/gif,image/jpeg,image/png,image/tiff,text/html,text/plain,application/vnd.cups-postscript,application/vnd.cups-raw,application/vnd.hp-hpgl,image/x-bitmap,image/x-photocd"
    "printer-type=0x90d4"
    "URF=none"
)

; ******************************

In this step, once you have all the services running, your iOS mobile device will already be able to detect the AirPrint enabled printers and to send print jobs to them. There is only one missing task: Trying to automatize the creation of the printer definitions in the zone definition file.

Monday, July 18, 2011

AirPrint in corporate networks

AirPrint is a component of Apple since iOS version 4.2 for printing via WiFi, whose main advantage is that AirPrint does not require printer-specific drivers.

The adoption of tablets is a fact nowadays, so the system administrators must provide some mechanism in order to allow tablet users to print. If we focus on iPad type of tablets, these are able to print via AirPrint, but just using a small set of selected printers.

Any already established company has an infrastructure of computers and printers. Adding a new kind of printers does mean buying new hardware and this is not an option in many sites. On the other side, in large corporate sites (so, do not think on a small office with just a few computers) the network is organized and divided, and wireless users do use different network ranges or VLANs than wired users. AirPrint does not work across subnets.

So, the challenge involves two main tasks:
1.- Allow existing printers to be compatible with the AirPrint mechanism.
2.- Allow the usage of wired printers from wireless devices even if they are in different networks, so traveling across different subnets.

Both topics have been already covered in other blogs, but so far, I haven't found a tutorial indicating all the necessary tasks.

Our scenary:
A middle size network with a few hundreds of wired computers. These computers are organized into several subnets.
Several APs giving wireless access to users. Printers are all on some of the network ranges dedicated to wired devices. The IP range for wireless devices form another different subnet with no printers at all.

The proposal is to configure a server to act as a gateway to allow AirPrint devices (those running iOS 4.2 or above) to use any previously existing printer from the corporate network. In order to give this service, we are going to install all the necessary components on a Linux box running CentOS 5.x (5.6 at the time of writing).

These steps are taken from an article of Ryan Finnie.

First step is to install all the printers to be "exported" in this server. This task is optional, the gateway could export any available printer (either network printers or those served by other computers). The goal of this first step is to centralize the administration of the printers, and to be able to differentiate the AirPrint related stuff from the rest of the system, in order to allocate the cause of possible problems. This step is also very recommendable in order to automatically generate in a further step some necessary configuration files.

So, we enable CUPS in the server (if not already done), then we setup the remote printers and finally we make some changes to allow print sharing. Additionally we allow the CUPS server listening on its public interface instead of just listening on localhost.

# Modifications to the file /etc/cups/cupsd.conf
Listen 10.36.11.22:631
ServerAlias *
Browsing On


As stated above, AirPrint does not require printer-specific drivers or setup tasks. AirPrint achieves this goal by means of bonjour, the Apple implementation of the zeroconf mechanism.
According to wikipedia, Zero configuration networking (zeroconf), is a set of techniques that automatically creates a usable Internet Protocol (IP) network without manual operator intervention or special configuration servers.

On the other side, Avahi is a free Zeroconf implementation available for Linux and BSDs. The solution is to use avahi to announce the desired printers.

Creation of the avahi configuration files for the printers is not an easy task. Luckily, Timothy J. Fontaine wrote a script to automatically generate the avahi configuration file from the CUPS printer definitions existing on the system. So, the next step is to download or copy that script in our system to create our own avahi configuration files with no effort.

In order to run this script, we need to install the package providing the bindings between CUPS and python.
In many systems this package is named "pycups", but in CentOS this is provided by "system-config-printer-libs". We proceed to install it.

# yum install system-config-printer-libs


The script requires python >=2.5, but since we are using CentOS 5.x, our system has just python 2.4.3 installed. If we try to execute the script, we will obtain the following error:

# python airprint-generate.py 
Traceback (most recent call last):
File "airprint-generate.py", line 46, in ?
raise 'Failed to find python libxml or elementtree, please install one of those or use python >= 2.5'
Failed to find python libxml or elementtree, please install one of those or use python >= 2.5


In this case, it becomes necessary to make some modifications to the script. This can be done applying this patch, or just editing the file and changing line #43

# vi airprint-generate.patch 
43c43
< from elementtree import Element, ElementTree, tostring
---
> from cElementTree import Element, ElementTree, tostring


Once created the patch file, we apply it to the script:

# patch airprint-generate.py airprint-generate.patch
# python airprint-generate.py


Once patched, we can run the script and we will obtain the files to configure avahi. Just copy these files to the avahi directory and reload (or restart) the service.

# cp AirPrint-*.service /etc/avahi/services/
# service avahi-daemon reload


In this step, you should be able to print via AirPrint if your CentOS has an IP on the same subnet of your iPxx devices. This may happen in small ofices or domestic networks, if the CentOS box uses a wireless connection, but it is not the usual scenario in medium or large size corporate networks. This second scenario will be covered in the next blog entry.

Sunday, July 10, 2011

Multiseat is coming back with ubuntu 11.10

Just a few days ago, the version alfa 2 of Ubuntu 11.10 was released. This version comes with some interesting changes, may be the most significant of them are the use of the branch 3 of the Linux kernel, as well as the use of Gnome 3.

As you probably already know, multiseat is closely related to display manager, since it is a key component which allows the starting of multiple simultaneous graphical sessions. You probably will know, too, that this feature was removed in gdm (the Gnome Display Manager) in version 2.22. This is the reason why since Ubuntu 9.10 there is no an easy setup to configure a multiseat computer.

In Ubuntu 11.10 LightDM has replaced GDM as the default display manager. This is a rather new display manager, so the first question was whether this new display manager would support a multiseat configuration. The question seems to have been answered and, accordingly to the comments, multiseat configurations will be possible.

We are currently preparing a computer with the alfa 2 of Ubuntu 11.10 just to test the multiseat setup. We will keep you informed about the results on the blog.

Meanwhile, you can have a look to the new features of Ubuntu 11.10

Thursday, March 31, 2011

Setup your own DLNA server

DLNA is a standards-based technology whose main goal is to ease the sharing of multimedia contents. DLNA is an open protocol based on UPnP.

This article describes how to install and configure a DLNA server. This software is going to be installed on a home server. Just as a comment, this home server is a very low power consumption machine (Atom N550 board) running a very powerful OS: 64 bit CentOS 5.5.

We started with a brief comparison and testing of some of the most popular DLNA servers for Linux uShare, MediaTomb and MiniDLNA). That was not an exhaustive comparison, but just a few quick tests. After that quick preview, we finally decided to use MiniDLNA.

The authors of the MiniDLNA project offer a statically compiled version, which makes very easy to install the software. Download it, and optionally, you can make some changes to file permissions in order to keep them consistent with the rest of your system.

Let's assume that you downloaded the installation package on /tmp

Previously to the installation, we set root as the owner of the files (optional) just to avoid having an unreferenced owner, then, we unpack the software.


# mkdir /tmp/mini
# tar -xzf /tmp/minidlna_1.0.19_static.tar.gz -C /tmp/mini
# chown -R root:root /tmp/mini
# cp -r /tmp/mini/* /


The configuration is really simple. You have a self-explaining configuration file (/etc/minidlna.conf) with a few options. Just indicate which interface are you running the service on, and the directory containing the media to share. In my case, these were the only changes I made, keeping the rest of the options with the default values.


network_interface=eth0
media_dir=/usr/local/media/
friendly_name=Multimedia


The most difficult task is to configure the firewall (iptables, since it is a CentOS based server) properly. This home server offers public services to the outside. External services are separated from internal ones, being offered through a different interface. Even so, disabling the firewall is not an option.

First of all, we launch the service just to examine the ports that must be kept open:


# /usr/sbin/minidlna
# lsof -i4 -n | grep minidlna
minidlna 12964 root 8u IPv4 1155301 UDP *:ssdp
minidlna 12964 root 9u IPv4 1155302 TCP *:trivnet1 (LISTEN)
minidlna 12964 root 10u IPv4 1155303 UDP 192.168.1.8:37167
minidlna 12965 root 8u IPv4 1155301 UDP *:ssdp
minidlna 12965 root 9u IPv4 1155302 TCP *:trivnet1 (LISTEN)
minidlna 12965 root 10u IPv4 1155303 UDP 192.168.1.8:37167
minidlna 12968 root 8u IPv4 1155301 UDP *:ssdp
minidlna 12968 root 9u IPv4 1155302 TCP *:trivnet1 (LISTEN)
minidlna 12968 root 10u IPv4 1155303 UDP 192.168.1.8:37167


Ports ssdp (1900/udp) and trivnet1 (8200/tcp) are proper of this service. The other port (37167/udp) varies in every execution.

I recognize not being an expert setting firewall rules, but firewall configuration in CentOS in this case resulted to be a very frustrating task.

After almost two whole days spent reading threads, tutorials and comments about UPnP, SSDP, DLNA, multicast, routing and filtering, I finally obtained a firewall configuration that, so far, seems to work properly. Neither more permissive than necessary nor too much complicated.

Maybe due to my limited knowledge about iptables, I was not able to introduce these rules from the command line to behave as I expected, so, I had to edit, finally, the /etc/sysconfig/iptables file in order to introduce these rules. I added two rules in the RH-Firewall-1-INPUT section, just before the REJECT rule.


...
-A RH-Firewall-1-INPUT -s 192.168.1.0/24 -d 192.168.1.8 -p tcp -m tcp --dport 8200 -j ACCEPT
-A RH-Firewall-1-INPUT -s 192.168.1.0/24 -d 239.255.255.250 -p udp -m udp --dport 1900 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT


The IP 192.168.1.8 is where the machine offers services to other devices in the LAN (192.168.1.0/24)

First rule allow computers on the LAN to access the server on the port 8200/tcp
Second rule allow computers on the LAN (including the same server) to access the SSDP service (1900/udp) on a fixed multicast address (239.255.255.250)

Restart iptables after these changes, and you will have a working DLNA server without compromising the security of your server.