Saturday, December 3, 2011
Reduce Linux laptop's backlight on boot up
I like Linux. Especially for being able to create graceful workarounds for the things we get accustomed in a real life.
Being a Linux-powered laptop owner I have always been annoyed by sharp backlight change when Linux boots up. I do not like when a backlight is set to the maximum one and there are no ways to affect these settings on. So I did a very funny trick.
System's backlight (aka "display's brightness") in Linux could be changed through the special /sys/class/backlight/acpi_video0/brightness file. When an integer is written Linux immediately changes laptop's backlight level. E.g. the following command:
The first steps of Linux boot up are made with initramfs scripts which are executed one by one. When system-required-specific modules are loaded laptop's backlight level dramatically raises up. So we can think about this point in time as a moment when the user is able to tell Linux not to do so; or at least set it explicitly to a preferred value. That moment in time in the terms of initrams-tools could be called "premount".
So here we go:
Being a Linux-powered laptop owner I have always been annoyed by sharp backlight change when Linux boots up. I do not like when a backlight is set to the maximum one and there are no ways to affect these settings on. So I did a very funny trick.
System's backlight (aka "display's brightness") in Linux could be changed through the special /sys/class/backlight/acpi_video0/brightness file. When an integer is written Linux immediately changes laptop's backlight level. E.g. the following command:
# echo 10 > /sys/class/backlight/acpi_video0/brightnesswill set display's brightness to 10 points.
The first steps of Linux boot up are made with initramfs scripts which are executed one by one. When system-required-specific modules are loaded laptop's backlight level dramatically raises up. So we can think about this point in time as a moment when the user is able to tell Linux not to do so; or at least set it explicitly to a preferred value. That moment in time in the terms of initrams-tools could be called "premount".
#!/bin/sh
### file: /etc/initramfs-tools/scripts/local-premount/backlight_level
### this file must have an executable bit.
###
### Author: Andrew Sichevoi
### Please feel free to send your bug reports to http://blog.thekondor.net
PREREQ=""
prereqs()
{
echo "${PREREQ}"
}
case "${1}" in
prereqs)
exit 0;
;;
esac
. /scripts/functions
DEFAULT_BACKLIGHT_LEVEL=10
BACKLIGHT_LEVEL=
for arg in $(cat /proc/cmdline); do
case ${arg} in
backlight_level=*)
BACKLIGHT_LEVEL=${arg#backlight_level=}
;;
esac
done
if [ -z ${BACKLIGHT_LEVEL} ]; then
log_warning_msg "Using default backlight level: '${DEFAULT_BACKLIGHT_LEVEL}'"
BACKLIGHT_LEVEL=${DEFAULT_BACKLIGHT_LEVEL}
fi
echo ${BACKLIGHT_LEVEL} > /sys/class/backlight/acpi_video0/brightness
So here we go:
- Add the below-mentioned backlight_level script to /etc/initramfs-tools/scripts/local-premount directory, make it executable;
- Update initramfs using update-initramfs command (Linux distribution specific; at least that works for Debian and Ubuntu) to include the script to the initrd image;
- Set passing backlight_level=X (where X is a preferred backlight level) boot option in your's grub.cfg.
and reboot to see the changes in action.
The script just looks up backlight_level kernel boot option and writes (if any, or default one otherwise) this value to the brightness file for immediate laptop's brightness update.
Simple? I think yes. Does it work? Definitely. Perfect? Not at all: I believe that system's boot's up settings should not differ from main DE's ones: they should be configured through the single entry point not several ones. Anyway this scripts makes feel us that we able to control Linux as well as we have a new task to think about :). So stay tuned!
The script just looks up backlight_level kernel boot option and writes (if any, or default one otherwise) this value to the brightness file for immediate laptop's brightness update.
Simple? I think yes. Does it work? Definitely. Perfect? Not at all: I believe that system's boot's up settings should not differ from main DE's ones: they should be configured through the single entry point not several ones. Anyway this scripts makes feel us that we able to control Linux as well as we have a new task to think about :). So stay tuned!
Friday, November 11, 2011
Python, make ConfigParser aware of spaces
There is a wonderful Python's module called ConfigParser which allows to process .ini-style configuration files easily. I prefer to use it everywhere rather than spend the time to implement my own solution. Recently there was a bug received that values with leading and trailing spaces are read incorrectly: spaces are lost. This might be important for cases when an application is sensitive for such values; e.g.: for passwords.
It was discovered that current Python's ConfigParser implementation cannot be tuned up not to strip values while reading a configuration. Also there was a corresponding issue found with an attached patch. Unfortunately the patch has not been applied to public available Python builds yet. Definitely it is absolute not convenient to patch Python everywhere where yours application is run.
The solution is not to lose leading and trailing spaces by wrapping them for quotes. Here is a helping code snippet to solve this issue:
Overridden get() method removes quotes if any. So for ConfigParser's client that is transparent if the option has a value with quoted spaces or not; double and single quotes are supported. set() does vice versa.
So it is enough to replace instantination of ConfigParser in your Python's code just with SpaceAwareConfigParser one and it should work like expected.
It was discovered that current Python's ConfigParser implementation cannot be tuned up not to strip values while reading a configuration. Also there was a corresponding issue found with an attached patch. Unfortunately the patch has not been applied to public available Python builds yet. Definitely it is absolute not convenient to patch Python everywhere where yours application is run.
The solution is not to lose leading and trailing spaces by wrapping them for quotes. Here is a helping code snippet to solve this issue:
class SpaceAwareConfigParser(ConfigParser.ConfigParser):
def __init__(self, **args):
KEEP_SPACES_KEYWORD = "keep_spaces"
self.__keep_spaces = args.get(KEEP_SPACES_KEYWORD, True)
args.pop(KEEP_SPACES_KEYWORD)
ConfigParser.ConfigParser.__init__(self, **args)
def get(self, section, option):
value = ConfigParser.ConfigParser.get(self, section, option)
if self.__keep_spaces:
value = self._unwrap_quotes(value)
return value
def set(self, section, option, value):
if self.__keep_spaces:
value = self._wrap_to_quotes(value)
ConfigParser.ConfigParser.set(self, section, option, value)
@staticmethod
def _unwrap_quotes(src):
QUOTE_SYMBOLS = ('"', "'")
for quote in QUOTE_SYMBOLS:
if src.startswith(quote) and src.endswith(quote):
return src.strip(quote)
return src
@staticmethod
def _wrap_to_quotes(src):
if src and src[0].isspace():
return '"%s"' % src
return src
Overridden get() method removes quotes if any. So for ConfigParser's client that is transparent if the option has a value with quoted spaces or not; double and single quotes are supported. set() does vice versa.
So it is enough to replace instantination of ConfigParser in your Python's code just with SpaceAwareConfigParser one and it should work like expected.
Labels:
code snippet,
python,
solution
Friday, October 28, 2011
PAM authentication for Transmission btorrent client
The one of the most valuable things in the World Wide Web beside the public available knowledge are files. Files which you can grab; and it does not matter they are Free or not. Files could be downloaded using various ways: when you grab them for yourself only or when not just grab but also provide others with the abilities to get them easily too. Due to the second point I like to use torrents. The one of my favorite BitTorrent clients is Transmission -- great OpenSource crossplatform software.
The one of the valuable features of Transmission is an ability to run as daemon not being depending on any kind of GUI toolkits. That's why it is common to use Transmission daemon on servers and various NASes. The daemon can be conveniently managed using any GUI client as well as a Web-interface or even command-line tool. The access to the daemon is controlled through credentials -- the pair of username and password which are stored in settings.json configuration file.
It is definitely cool to be able to protect my running Transmission daemon from my neighbour who works for RIAA :). But I do not like the idea that to manage the daemon safely I should to generate and keep in mind another access password. I asked myself why just not to use the credentials I already have? I mean credentials I already use to log to my Linux system in.
In Linux user's authentication to a system usually is performed using PAM infrastructure -- Pluggable Access Module. The infrastructure provides a single mechanism for managing and embedding of software applications to the authentication workflow.
So I decided to integrate PAM authentication for Transmission torrent daemon. And it was done for 1 day. The patch is available on my git repository. It is applicable for trunk@13057.
Enable PAM authentication for Transmission
The one of the valuable features of Transmission is an ability to run as daemon not being depending on any kind of GUI toolkits. That's why it is common to use Transmission daemon on servers and various NASes. The daemon can be conveniently managed using any GUI client as well as a Web-interface or even command-line tool. The access to the daemon is controlled through credentials -- the pair of username and password which are stored in settings.json configuration file.
It is definitely cool to be able to protect my running Transmission daemon from my neighbour who works for RIAA :). But I do not like the idea that to manage the daemon safely I should to generate and keep in mind another access password. I asked myself why just not to use the credentials I already have? I mean credentials I already use to log to my Linux system in.
In Linux user's authentication to a system usually is performed using PAM infrastructure -- Pluggable Access Module. The infrastructure provides a single mechanism for managing and embedding of software applications to the authentication workflow.
So I decided to integrate PAM authentication for Transmission torrent daemon. And it was done for 1 day. The patch is available on my git repository. It is applicable for trunk@13057.
Enable PAM authentication for Transmission
- Add a user which privileges the daemon is run on to the group which is able to read /etc/shadow file (if authentication in your system is performed in standard Linux way). Usually such group is called 'shadow'.
- Add to your Transmission's settings.json "rpc-native-authentication-enabled" parameter set to "true".
- Restart the daemon and try to re-connect using your system credentials.
The name of parameter is chosen in that way because of cross-platform intentions. For each system native authentication might differ from PAM. But most UNIX`es it will most probably be PAM.
Currently the feature is enabled for Linux only. I suppose it can work w/o any extrac magic on MacOS as well as on BSD.
Cons
- ssh-ready credentials are passed over network in the non-encrypted way;
For all my cases the credentials cannot (again: for my cases only) be sniffed. For other possible cases just use ssh-powerless credentials. And finally start using ssh key-based authentication!
- the daemon is able to read /etc/shadow file. If the daemon is taken over, a brute force attack to the password can performed.
If your PAM is configured in the way no to use pam_unix.so, there should be no problems. In other ways there could be some unless you are using one-time passwords.
Pros
- easy to maintain access to control Transmission daemon itself as well as allow 3rd-party trusted people to manage processed torrents;
No need to keep in mind another password. No extra reasons to change the password in cases when you shared daemon access for one time; just block guest account.
- PAM is just a frontentd but there could be any backend;
In the other words you are not forced to used system credentials to manage Transmission daemon. For example your PAM could be configured to perform user's authentication over LDAP or one-time SMS.
Using
The patch to add PAM authentication to Transmission torrent client is distributed in terms of GNU GPL v3.0+ license.
Since to its specific needs currently it is not going to be the part of main Transmission's branch. So if you maintain your own Transmission's fork/build please feel free to share your opinions regarding the feature.
Labels:
linux,
patch,
transmission
Thursday, June 23, 2011
actkbd package for Debian/Ubuntu
Recently I became an owner of Thinkpad X120E laptop produced by Lenovo. The computer was supplied with no pre-installed OS so I deployed (unexpected decision, huh!) Debian GNU/Linux there. This time I decided not to use desktop "ready-to-use-out-of-the-box" environments like Gnome or KDE and give a try to build a DE I want from bricks.
The one of the tasks I have faced with is how to make ThinkPad X120E hotkeys work in Debian GNU/Linux in a uniform way - to have a single keybindings entry point; which should work in VT w/o X.org also. Some of the keys (like VolUp, VolDown, Suspend etc) can be successfully handled through acpid daemon but the rest ones (say enable/disable microphone or switch touchpad/trackpoint) can be with xbindkeys (or similar) application only.
After a short research I found an amazing application called actkbd -- a keyboard shortcut X.org independent daemon. Thanks for being dependent on evdev Linux interface actkbd allows to handle ACPI events as well. But unfortunately there have been no ready-to-use Debian (Ubuntu) package available on the Internet (*).
(*) Of course I could install it using "configure && make && make install" way but I did not want to transform my Debian GNU/Linux installation to Slackware or kind of :).
Hence I made actkbd Debian/Ubuntu package by myself. So you can download here already built actkbd package for Debian Wheezy/i386 or build it by yourself (using dpkg-buildpackage). I am not so experienced in applications debianizing but seems it works well. Please feel free to use it or even to push to Debian/Ubuntu repositories.
After a short research I found an amazing application called actkbd -- a keyboard shortcut X.org independent daemon. Thanks for being dependent on evdev Linux interface actkbd allows to handle ACPI events as well. But unfortunately there have been no ready-to-use Debian (Ubuntu) package available on the Internet (*).
(*) Of course I could install it using "configure && make && make install" way but I did not want to transform my Debian GNU/Linux installation to Slackware or kind of :).
Hence I made actkbd Debian/Ubuntu package by myself. So you can download here already built actkbd package for Debian Wheezy/i386 or build it by yourself (using dpkg-buildpackage). I am not so experienced in applications debianizing but seems it works well. Please feel free to use it or even to push to Debian/Ubuntu repositories.
- actkbd Debian "Wheezy"/i386 GNU/Linux package (testing): download
- actkbd Debian/Ubuntu package sources:
Sunday, May 22, 2011
Python, imp.load_source() trap
While I have been writing a hook for WebApy lightweight RESTful Python webserver -- the recent a project of mine, I got ran into the funny (actually it wasn't; since it was hard enough to debug) issue related to loading of Python-app addons. As far as you know (or not; if you already have taken a look at sources), there is Python's standard library's 'imp' module is used to load hook files. "imp.load_source()" if to be more precised.
Here is an example of the problem you may get into in Python while loading modules with load_source() function of imp module.
Lets see a simplified example of what where I ran into. Suppose our Python application supports external addons (aka plugins/extensions). Each addon must provide a class named "Addon" with implemented "runLogic()" method. Addons have priorities -- from 1 to N (N > 1); priority set to "1" is a highest one. Suppose several of addons also implement internal Helper class for low-level dirty work.
Addon #1 (file: addon-1.addon.py)
and Addon #2 (file: addon-2.addon.py)
Addons are handled inside the main application through the class named "AddonLoader". "AddonLoader" is initialized with the only argument -- filename pattern of modules; since our modules are called addon-1.addon.py and addon-2.addon.py respectively the aforementioned pattern could be '*.addon.py'. Also "AddonLoader" provides with the only public method "runMoreImportantAddon()" which executes an addon with the highest priority. Addons handling is implemented through Python's 'imp' module:
So could you predict what the output will be when main app is ran? I bet that probably not. There will be an exception that Helper class could not be instantiated because of invalid passed parameters amount. Was it expected?
No, it was not; at least for me. The problem of this code is hidden in "name" argument of imp.load_source()'s function. For each enumerated addon it is still the same (set explicitly to "addon"); on the each iteration all already loaded classes are overwritten. On first iteration we extract and keep a reference to Addon #1 (do not forget that it uses Helper #1 class). On the second (final) iteration a reference to Addon #2 (it depends on Helper #2 class) is taken. Since loaded addons are set to have the same name ("addon"), on the final iteration we see that Helper #1 is overwritten with Helper #2 in the namespace of "addon". And when Addon #1 is being instantiated it is calls for Helper class, but as you remember it was overwritten and does not take any parameters in the constructor. Here we get an exception.
The solution of the problem is not to pass constant name to load_source() function. Just replace "addon" there with the call of the function which returns an unique name of module to load. Names must be unique!
Since the official documentation does say nothing about uniqueness of "name" argument, I wonder why there is no hint about the trap you could get into while using it?
Here is an example of the problem you may get into in Python while loading modules with load_source() function of imp module.
Lets see a simplified example of what where I ran into. Suppose our Python application supports external addons (aka plugins/extensions). Each addon must provide a class named "Addon" with implemented "runLogic()" method. Addons have priorities -- from 1 to N (N > 1); priority set to "1" is a highest one. Suppose several of addons also implement internal Helper class for low-level dirty work.
Addon #1 (file: addon-1.addon.py)
class Helper(object):
""" Internal, addon-specific helper class """
def __init__(self, msg):
self.__msg = msg
def help(self):
print self.__msg
class Addon(object):
""" Addon entry point class """
PRIORITY = 1
def runLogic(self):
addonHelper = Helper("module 1")
addonHelper.help()
and Addon #2 (file: addon-2.addon.py)
class Helper(object):
""" Internal, addon-specific helper class """
def help(self):
print "module 2"
class Addon(object):
""" Addon entry point class """
PRIORITY = 2
def runLogic(self):
addonHelper = Helper()
addonHelper.help()
Addons are handled inside the main application through the class named "AddonLoader". "AddonLoader" is initialized with the only argument -- filename pattern of modules; since our modules are called addon-1.addon.py and addon-2.addon.py respectively the aforementioned pattern could be '*.addon.py'. Also "AddonLoader" provides with the only public method "runMoreImportantAddon()" which executes an addon with the highest priority. Addons handling is implemented through Python's 'imp' module:
import glob ### To find addons on local filesystem
import imp ### To load addons
class AddonLoader(object):
def __init__(self, addonPattern):
self.__addonPattern = addonPattern
def runMoreImportantAddon(self):
""" Runs an addon with the highest priority (determined by PRIORITY property) """
moreImportantAddon = self.__getMoreImportantAddonInstance()
moreImportantAddon.runLogic()
def __getMoreImportantAddonInstance(self):
""" Returns an addon instance with the highest (1 -- high, 10 -- low) priority """
availableAddons = self.__enumerateAddonsOnFileSystem()
sortedAvailableAddons = sorted(availableAddons,
key = lambda addon: addon.PRIORITY)
return sortedAvailableAddons[0]()
def __enumerateAddonsOnFileSystem(self):
""" Load addons by specified pattern and returns a list with them """
addons = list()
for addonPath in glob.glob(self.__addonPattern):
addon = imp.load_source("addon", addonPath)
addons.append(addon.Addon)
return addons
def main():
addonLoader = AddonLoader("*.addon.py")
addonLoader.runMoreImportantAddon()
if "__main__" == __name__:
main()
So could you predict what the output will be when main app is ran? I bet that probably not. There will be an exception that Helper class could not be instantiated because of invalid passed parameters amount. Was it expected?
No, it was not; at least for me. The problem of this code is hidden in "name" argument of imp.load_source()'s function. For each enumerated addon it is still the same (set explicitly to "addon"); on the each iteration all already loaded classes are overwritten. On first iteration we extract and keep a reference to Addon #1 (do not forget that it uses Helper #1 class). On the second (final) iteration a reference to Addon #2 (it depends on Helper #2 class) is taken. Since loaded addons are set to have the same name ("addon"), on the final iteration we see that Helper #1 is overwritten with Helper #2 in the namespace of "addon". And when Addon #1 is being instantiated it is calls for Helper class, but as you remember it was overwritten and does not take any parameters in the constructor. Here we get an exception.
The solution of the problem is not to pass constant name to load_source() function. Just replace "addon" there with the call of the function which returns an unique name of module to load. Names must be unique!
Since the official documentation does say nothing about uniqueness of "name" argument, I wonder why there is no hint about the trap you could get into while using it?
Wednesday, May 11, 2011
WebApy -- webserver for easy and rapid REST API imitation
Have been developing an application which depends on a remote REST (stands for Representational State Transfer) API of one of popular services I ran into the need to use it [API] more intensively while testing/debugging the code. Not all remote services provide developers with SandBox`ed environments to play in. And not all services may tolerate frequently repeated requests to their REST API; they may just ban your access.
The best way to avoid such problems is to imitate remote API locally.
WebApy -- is a simple lightweight python Webserver built on the top on BaseHTTPServer. WebApy allows easily to create REST API which pretends like original one.
When it might required
The final code will look like:
Seems pretty easy.
Test
Lets use "curl" utility to test how "our" API works. A malformed request:
And the correct one:
Distribution
WebApy REST API webserver can be directly downloaded from the git repository: http://git.thekondor.net/webapy.git. The software is licensed in terms of GNU GPL v3 and higher.
General notes
The best way to avoid such problems is to imitate remote API locally.
WebApy -- is a simple lightweight python Webserver built on the top on BaseHTTPServer. WebApy allows easily to create REST API which pretends like original one.
When it might required
- To develop Unit tests for a library/application which interacts with remote REST API;
- To debug such library/application more intensively and not being dependent on remote service availability;
- To make a fast
dirtyprototype of REST API for your service.
- Create a file (or several ones) called "hook" -- a regular Python file with pre-defined structure (the sample of such hook is available in hooks/ dir; "hooks" must have "hooks.py" filename extension);
- Implement canHandleRequest() static method which tells WebApy that the hook can handle this request;
- Implement code(), headers() and data() methods to return corresponding response values on the passed request;
- Run WebApy server instance to serve your application/library with imitated REST API.
Example
Last.Fm is world's largest and well known online music catalogue. It has a remote XML API to access to theirs music information database. Before start working with the API a client application should perform several authentication steps:
Accordingly to Last.Fm developer's documentation an Auth token retrieving is performed via auth.getToken request. It has only one mandatory parameter -- "api_key" (a Last.Fm API key; it can be received upon request to Last.Fm).
Step 1
First we should check inside the hook if it can handle received API request. The hook can handle request if the following conditions are met:
Lets implement canHandleRequest():
Step 2
Second and final step: implement a logic to return a corresponding response to REST API client. Suppose only a client with Last.fm API key b25b959554ed76058ac220b7b2e0a026 is able to get Auth token. For other ones an error must be returned:
Last.Fm is world's largest and well known online music catalogue. It has a remote XML API to access to theirs music information database. Before start working with the API a client application should perform several authentication steps:
- Retrieve an Auth token;
- Retrieve an Auth session (providing user's credentials and obtained Auth token).
Accordingly to Last.Fm developer's documentation an Auth token retrieving is performed via auth.getToken request. It has only one mandatory parameter -- "api_key" (a Last.Fm API key; it can be received upon request to Last.Fm).
Step 1
First we should check inside the hook if it can handle received API request. The hook can handle request if the following conditions are met:
- The request is GET [type];
- There is "method" argument passed;
- The value of "method" argument is "auth.gettoken".
Lets implement canHandleRequest():
@staticmethod
def canHandleRequest(request):
if "GET" != request.method:
return False
return "auth.gettoken" == request.simpleQuery.get("method", [None])[0]
Step 2
Second and final step: implement a logic to return a corresponding response to REST API client. Suppose only a client with Last.fm API key b25b959554ed76058ac220b7b2e0a026 is able to get Auth token. For other ones an error must be returned:
def __makeResponse(self):
apiKey = self.request.simpleQuery.get("api_key", [None])[0]
if self.isValidApiKey(apiKey):
self.__response = make_authenhicated_response(authToken = "cf45fe5a3e3cebe168480a086d7fe481")
else:
### Error code "10" stands for invalid API key
self.__response = make_failed_authenhicated_response(errorCode = 10)
The final code will look like:
import string
RESPONSE_TPL = string.Template("<?xml version=\"1.0\" encoding=\"utf-8\"?><lfm status=\"${code}\">${body}</lfm>")
def make_authenhicated_response(authToken):
return RESPONSE_TPL.substitute(code = "ok", body = "<token>%s</token>" % authToken)
def make_failed_authenhicated_response(errorCode):
return RESPONSE_TPL.substitute(code = errorCode, body = str())
class RequestHook:
@staticmethod
def canHandleRequest(request):
if "GET" != request.method:
return False
return "auth.gettoken" == request.simpleQuery.get("method", [None])[0]
def __init__(self):
self.__response = None
self.__makeResponse()
def __makeResponse(self):
apiKey = self.request.simpleQuery.get("api_key", [None])[0]
if self.isValidApiKey(apiKey):
self.__response = make_authenhicated_response(authToken = "cf45fe5a3e3cebe168480a086d7fe481")
else:
### Error code "10" stands for invalid API key
self.__response = make_failed_authenhicated_response(errorCode = 10)
def code(self):
return 200
def headers(self):
return {}
def data(self):
return self.__response
@staticmethod
def isValidApiKey(key):
return "b25b959554ed76058ac220b7b2e0a026" == key
Seems pretty easy.
Test
Lets use "curl" utility to test how "our" API works. A malformed request:
$~ curl 'http://localhost:8080/?method=auth.gettoken'
And the correct one:
$~ curl 'http://localhost:8080/?method=auth.gettoken&api_key=b25b959554ed76058ac220b7b2e0a026'cf45fe5a3e3cebe168480a086d7fe481
Distribution
WebApy REST API webserver can be directly downloaded from the git repository: http://git.thekondor.net/webapy.git. The software is licensed in terms of GNU GPL v3 and higher.
General notes
Imitated REST API can return JSON as well as XML responses (actually anything; depends on your needs). WebApy is not intended to serve production environment, for debugging and testing purposes only since it was developed as an accessorial part of another project of mine. Hence it has some limitations and things to improve (especially I want to replace canHandleRequest() with the declarative description). Documentation is coming soon.
Anyway please feel free to submit your bug reports if any.
Anyway please feel free to submit your bug reports if any.
Sunday, May 1, 2011
Broken Java networking in Debian
Playing around with Java I ran into the problem well known as Bug #560044. Seems that the problem appears for Debian GNU/Linux only. The essence of the issue is: when you run any Java application that requires network access (by HTTP or by other protocol; it does not matter) you will (as well as the running application itself) receive the following error. Or a very similar one:
Exception in thread "main" java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
This stack trace says nothing specific about occurred error except that a connection with a remote server cannot be established. The problem is at the bottom of explicitly set in Debian bindv6only kernel parameter. When it turned on (set to "1" in terms of sysctl) it means that network-requesting application will receive IPv6 socket by default not IPv4. To be able to use IPv4 socket an application should open it explicitly. Since some applications rely on the host system's settings it leads that they may fail while network access (details).
The solution is pretty easy: to disable explicit IPv6 binding in your Debian GNU/Linux. This can be done by the command execution:
After this fix (I believe that it is still a w/a) Java applications can have network access and not raise incomprehensible exceptions anymore.
Exception in thread "main" java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
This stack trace says nothing specific about occurred error except that a connection with a remote server cannot be established. The problem is at the bottom of explicitly set in Debian bindv6only kernel parameter. When it turned on (set to "1" in terms of sysctl) it means that network-requesting application will receive IPv6 socket by default not IPv4. To be able to use IPv4 socket an application should open it explicitly. Since some applications rely on the host system's settings it leads that they may fail while network access (details).
The solution is pretty easy: to disable explicit IPv6 binding in your Debian GNU/Linux. This can be done by the command execution:
echo 0 | sudo tee -a /proc/sys/net/ipv6/bindv6onlyThis snippet will fix the problem for the current session only. To make the changes in Debian permanent the another one should be performed (proposed by Heikki Henriksen):
sudo sed -i 's/net.ipv6.bindv6only\ =\ 1/net.ipv6.bindv6only\ =\ 0/' /etc/sysctl.d/bindv6only.conf && sudo invoke-rc.d procps restart
After this fix (I believe that it is still a w/a) Java applications can have network access and not raise incomprehensible exceptions anymore.
Subscribe to:
Posts (Atom)
