Should I Buy A G1?
If you want a phone with good battery life, this is definitely not the phone for you. If you have GPS and Wifi and 3G and all the bells and whistles turned on and you're using the phone constantly, you'll probably only get four or five hours of use out of it. I turn GPS and Wifi off and turn the screen brightness down (because even when it's turned right down it's pretty bright), and don't use it non-stop, just a fair bit of twittering and the odd bit of internet browsing and some texts and phone calls. And usually it's down to around 40% battery by the end of the day. Of course, it depends completely on how you use it. I usually plug the phone in at night so that it's fully charged in the morning. Last night I forgot to switch the charger on, and when I checked in the morning it had used less than 5% of the battery, so it's standby use is pretty good.
If you want a phone which was take videos, this isn't (yet) the phone for you. There are rumours of software upgrades on the way which will add this capability, but it isn't there yet.
If you want a phone with an on-screen keyboard, this also isn't (yet) the phone for you. It's apparently on the way though. An I don't understand why anyone would rather use an on-screen keyboard when you can use the G1's fantastic real keyboard. I didn't think I'd like using the keyboard much, but it works brilliantly, at least for my hands. The 'chin' on the phone gets only very slightly in the way. And my phone is a black one - I've heard the letters are hard to see on the white and bronze versions.
If you want a phone which doesn't force you to have a Google account, this isn't the phone for you. The first thing you have to do when you set up your phone is either enter your Google account details, or sign up for a new account. The phone is very tied into the Google way of doing things. This isn't that much of a problem for me - I'm not Googlephobic. Nor am I a Googlephile. Most of my email still goes through my own server, and there is software for the G1 which lets me read that email just fine. And I was starting to use Google calendar just before I got the G1, so it's quite nice that it is now synched up with the phone. Some people will find the Googleness of the phone to be a hindrance, but I find it works really smoothly, and is around three million times better than my previous Nokia when it comes to synchronization.
If you want something which is thin and stylish, this probably isn't the phone for you. It isn't huge, but it's certainly no iphone. It's not ugly either - personally I think it's really well designed and good looking. Again, it's up to the beholder and their eye.
If you want a phone with an incredibly flexible operating system, this is the phone for you. The Android OS is very well designed, and the way it operates is mostly very intuitive. And unlike Nokia phones, where it seems that once you have the phone, you won't get any new features until you buy a new phone, the T-Mobile say that the G1 will be actively updated with new versions of software.
If you want to write software for your phone (in Java), this is the phone for you. Writing Android software is very easy, and Linux is a first class operating system when it comes to their development tools. Some parts of the SDK aren't quite finished, but that's because Google hadn't got them 100% stable before releasing the phone, and thought it'd be better to release a stable incomplete SDK that a buggy complete SDK, and I think that's a good way for things to be.
If you want a phone which is exceptionally useful, this is the phone for you. There is already a huge amount of software available for it, and although there's a fair amount of rubbish, there's also loads of very useful stuff, and I'm finding the phone more and more indispensable.
If you want to be in on the ground floor with the mobile operating system of the future, this is the phone for you. I genuinely believe that Android is the way to go with mobile phones, and have no regrets about getting this phone. And I purposefully got a twelve month contract rather than anything longer, because I'm pretty sure that by this time next year there'll be Android phones out there which are ten times better than this one!
The phone's main negatives for me: poor battery life; not enough internal memory; sometimes a little sluggish going back to the home screen (I think it's probably garbage collecting); a few minor niggles with some of the core software (but at least I can branch the code and write a better version if I want to).
The phone's main positives for me: bright, high contrast, high definition screen; real keyboard; sensitive (capacitive) touch screen; well designed operating system; mostly well designed software; easy syncing with Google; infinite possibilities; easy to use; fun to write software for it.
RTFM!
Everybody knows. It's written in all programming languages textbooks (I hope). Always check function return values. As you learn more and more, you learn that there are two types of functions: those that need to be checked (I'll refer to them as important functions) and other functions.
Even though it is sometimes discutable (and depending of lazyness and carelessness of specific developer) which group a function belongs to, there are many functions where no such discussion is needed.
One example of important function is EVP_VerifyFinal() which is part of openSSL library. The function is used as part of signature verification, so (hopefully) only complete idiot would dare not to check the return value.
Since return values are described clearly in the documentation, I'm surprised that the return values are not (correctly) checked. The only reason I can think about so far is that the author did not read the documentation. Since the function is really important (it's part of secure connection initialization!), author should use it in a proper way. Which means - according to documentation...
Conclusion: RTFM!
Barriers, barriers, barriers!
The shaders allow for optimization to run multiple so-called clauses in parallel. There are ALU clauses, fetch clauses, and special single instructions e.g. for exporting data that are considered clauses as well. Thing is, if you do not explicitly tell the engine to stall for the results of a clause (by BARRIER(1)), it will happily run this clause concurrently to the last one. On all but the most basic ASICs (read: RV610 and M72 didn't show any issues).
Now this can create really weird side effect. Just imagine the following trivial vertex shader (some strange symbolic notation here):
CF_INST_VTX fetch clause: buffer id0 FLOAT_32_32 -> GPR0
EXPORT_POS export insn: GPR0 -> vertex position
It basically should just fetch the vertices and pass them unchanged.
However, if you forget the barrier at the EXPORT_POS statement, the chip will concurrently fetch a vertex position into GPR0 and export the current contents of GPR0 to the rasterizer. Which means that the rasterizer can get arbitrary input. In my very case I couldn't believe my eyes - each time I ran the test program I got a rendering with the vertices of the test 4 to 12 calls ago, depending on the actual chip! Turns out that the chip assigns the 128 (or whatever is configured) vertex shaders in a round-robin scheme to the individual vertices and keeps the contents of the registers per shader unit. After 5 or so turns I got - by accident! - a correct set of coordinates through to the rasterizer, only it was pretty old by then.,,
Set the barrier, and all this is of no issue.
Alex has seen similar things in the ALU clauses, in his case it was forgetting to set the LAST() bit as an end-of-vector-unit flag. On r6xx it worked fine, on r7xx it barfed.
Python daemon threads considered harmful
The other day at work we encountered an
unusual exception in our nightly pounder test run after landing some
new code to expose some internal state via a monitoring API. The
problem occurred on shutdown. The new monitoring code was trying to
log some information, but was encountering an exception. Our logging
code was built on top of Python’s
logging module, and
we thought perhaps that something was shutting down the logging system
without us knowing. We ourselves never explicitly shut it down, since
we wanted it to live until the process exited.
The monitoring was done inside a daemon thread. The Python docs say only:
A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left."
Which sounds pretty good, right? This thread is just occasionally grabbing some data, and we don’t need to do anything special when the program shuts down. Yeah, I remember when I used to believe in things too.
Despite a global interpreter lock that prevents Python from being
truly concurrent anyway, there is a very real possibility that the
daemon threads can still execute after the Python runtime has started
its own tear-down process. One step of this process appears to be to
set the values inside globals()
to None, meaning that any module resolution results in an
AttributeError attempting to dereference NoneType.
Other variations on this cause TypeError to be thrown.
The code which triggered this looked something like this, although with more abstraction layers which made hunting it down a little harder:
try:
log.info("Some thread started!")
try:
do_something_every_so_often_in_a_loop_and_sleep()
except somemodule.SomeException:
pass
else:
pass
finally:
log.info("Some thread exiting!")
The exception we were seeing was an AttributeError on the
last line, the log.info() call. But that wasn’t even the
original exception. It was actually another AttributeError
caused by the somemodule.SomeException dereference. Because
all the modules had been reset, somemodule was None
too.
Unfortunately the docs are completely devoid of this information, at least in the threading sections which you would actually reference. The best information I was able to find was this email to python-list a few years back, and a few other emails which don’t really put the issue front and center.
In the end the solution for us was simply to make them non-daemon
threads, notice when the app is being shut down and join them to the
main thread. Another possibility for us was to catch
AttributeError in our thread wrapper class – which is what
the author of the aforementioned email does – but that seems like
papering over a real bug and a real error. Because of this
misbehavior, daemon threads lose almost all of their appeal, but oddly
I can’t find people really publicly saying “don’t use them” except in
scattered emails. It seems like it’s underground information known
only to the Python cabal. (There is no
cabal.)
So, I am going to say it. When I went searching there weren’t any helpful hints in a Google search of “python daemon threads considered harmful”. So, I am staking claim to that phrase. People of The Future: You’re welcome.
Maemo Music Player Client (MMPC) for Diablo
Maemo Music Player Client (MMPC) for Diablo
Finally I took the time to respond to multiple requests about providing mmpc 0.2.1 packages for Diablo. That also gave me the possibility to try the maemo autobuilder to upload my packages to the extras repository. And well, it worked out. It's especially useful for those people not running debian based systems, because with autobuilder you don't have to care about signing and uploading the packages with debian tools like debsign and dput.
So, this is not a new release, these are just new packages build for the Diable 4.1 distribution. I still did not manage to find the time to enhance mmpc further. There are just too many other things like work and university I have to care about. Either download the packages from Maemo Garage, or just get it through the extras repository as usual.
Driving the D-Bus with Ruby
Having looked at the D-Bus from the client perspective before, its now time to get behind the wheel.
In order to drive the D-Bus, you need to decide on a couple of things:
- which bus to drive
you can choose between the system(-wide) bus or the (per-)session bus - the service name
this is the name other clients can find your service, the name has dotted notation, e.g. my.awesome.Service - the objects to publish
each service can provide any number of objects, denoted a slash-separated path name, e.g. /my/awesome/Service/thing - the interface name
methods offered by objects are grouped by interface name. Usually its used to flag object capabilities. Interface names have dotted notation, usually prefixed by the service name, e.g. my.awesome.Service.Greeting - the methods
these are the exported functions of the objects. Methods, also called members in D-Bus speak, have input parameters and a return value.
Obtaining a drivers license
Allowance for driving the D-Bus is controlled by config files below /etc/dbus-1. /etc/dbus-1/system.conf controls the system bus, /etc/dbus-1/session.conf controls the session bus. Both load additional configuration files from sub-directories (/etc/dbus-1/system.d/ resp. /etc/dbus-1/session.d/)
These config files are xml-based busconfig snippets, defining policies on who's allowed to own a service who can use which interfaces of the service.
A typical service configuration looks like this
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="my.awesome.Service" />
<allow send_destination="my.awesome.Service" />
</policy>
<policy context="default">
<allow send_destination="my.awesome.Service"
send_interface="my.awesome.Service.Greeting" />
<!-- introspection is allowed -->
<allow send_destination="my.awesome.Service"
send_interface="org.freedesktop.DBus.Introspectable" />
</policy>
</busconfig>
It gives root the right to own the service my.awesome.Service and to use it as a client. Any other user can only use the my.awesome.Service.Greeting
interface of my.awesome.Service and can introspect it.
Convention tells you to name this busconfig file after your service with a .conf extension, e.g my.awesome.Service.conf. Place it below /etc/dbus-1/session.d/ or /etc/dbus-1/system.d/, depending which bus you want to drive.
Coding a D-Bus service
Examples for creating a D-Bus service using C are scarce and scary. Esp. programming in C while avoiding Glib, things become really ugly.
As we will see, using the ruby-dbus extension to Ruby makes creating D-Bus services a breeze.
We start with creating a DBus::Object defining (dbus_)methods under a (dbus_)interface.
#!/usr/bin/env ruby
require 'dbus'
class Awesome < DBus::Object
# Create an interface.
dbus_interface "my.awesome.Service.Greeting" do
dbus_method :hola, "in name:s" do |name|
puts "Hola #{name}!"
end
dbus_method :hello, "in name:s, out res:s" do |name|
"hello #{name}!"
end
dbus_method :ahoj, "in name:s, in title:s" do |name,title|
puts "Ahoj #{title} #{name}!"
end
end
end
The dbus_method call get the method name passed as a symbol, followed by its signature. The signature describes the input ("in") and output ("out") parameters by name (before the colon) and type (after the colon). The parameter names are just syntactic sugar, but the type is essential and enforced by D-Bus. Look at D-Bus type signatures to find out how to specify types.
ruby-dbus uses the method name and signature to automatically generate an XML representation of the interface needed for D-Bus introspection. This allows clients to find out about available interfaces and their methods at runtime.
The following do ... end block contains the method implementation. Every "in" parameter is passed as a value to the block in typical ruby fashion.
Next is connecting to the bus and obtaining the service. You can easily export multiple services if the bus configuration allows that.
# Choose the bus (could also be DBus::session_bus)
bus = DBus::system_bus
# Define the service name
service = bus.request_service("my.awesome.Service")
Then we create the object and export it through the service.
# Set the object path
obj = Awesome.new("/my/awesome/Service/thing")
# Export it!
service.export(obj)
Note that there is no requirement to create and export objects in advance. You can start with a simple object and method, creating additional objects as requested by the client. Hal is a typical example for such a service.
Finally we start the listener and wait for incoming requests.
# Now listen to incoming requests main = DBus::Main.new main << bus main.run
main.run never returns, as you would expect from a service daemon. Either kill(1) it, code a timeout signal or implement a method calling exit.
Running the service
Running the D-Bus service is as easy as running any other Ruby program:
kkaempf> ruby my_service.rbThis, however, will get you a Exception `DBus::Error' at (eval):26 - org.freedesktop.DBus.Error.AccessDenied: Connection ":1.967" is not allowed to own the service "my.awesome.Service" due to security policies in the configuration file
Looking at our busconfig defined above immediately reveals the error. Only user root is allowed to own the service. So we better do a
kkaempf> sudo ruby my_service.rbto get this running.
Now congrats for passing your drivers exam ! ;-)
Not a Good Start Into a Problematic Year
KDE Project:
Like some other [open]SUSE developers I was casted and am now forced to look for a new day job. It could have happened in better economic times for sure. :-(
Pointers to new interesting job positions are gladly accepted. Bonus points the more they have to do with Open Source, Linux, Qt and KDE.
openSUSE-like update repositories for third party projects
Starting with 11.0, the openSUSE-Education project hosts it’s own, separate update repository. This is our solution for the strategic decision not to use the openSUSE Build Service as repository for endusers but for development only.
So for production purposes, we always recommend to use our frozen repositories on http://download.opensuse-education.org/. But as “frozen” implies, the repositories there are frozen at the time, the openSUSE-Education team declares them as “Goldmaster” (which is the case for all except the 11.1 repo at the moment) – and no package update or changes happens for this repositories.
The openSUSE-Education team has relatively long development and testing cycles – but as everywhere, shi* happens, and so it might be that some of the packages in the frozen repository are broken or need a security fix. For this, we have created update repositories (for at least 11.0 and the upcomming 11.1 Edu-Release) which are disabled per default, but added to the system during installation of the openSUSE-Education-release package. (Reason behind this decision: if an administrator installs openSUSE-Education in a school, he wants to “mirror” the update repositories and not point every client to the official ones. All a user has to do is to enable this update repository via YaST or via “zypper mr -e ‘openSUSE-Education Updates'”.
We’re using the “updateinfo.xml” file formal described in the openSUSE-Wiki. Currently, we’ve 5 package updates/fixes for 11.0 in the update repository – and this might grow over the time. The updates are shown in the current online-update-applets as “normal” updates like the openSUSE ones. Interestingly, the user can’t see if an update is from the official openSUSE or the openSUSE-Education update repository – even if we use a different “from” tag. Perhaps we have to “play” with the “release” or other tags: testing is needed as it looks like nobody tries this before…
update
Bought some new digitial cameras. Two cheap from eBay for gphoto2 testing, and one Nikon D90 to replace the D70s to take good pictures. Not much pictures taken yet with the latter one.
Spent two trainrides with more improvements of the libgphoto2 API to come to a clearer API and one without fixed size buffers...
My API adjustements are not done yet though. One new CameraFile type (beside memory and fd style) missing, and some more cleanups necessary.
And I am ready for spring, just the weather throws snow at us still here in Nuernberg.
So much for Friday afternoon.
NTP With Autokey Authentication
Recently I had to verify the functionality of NTP server with autokey authentication. Though I thought that it would be simple, it took me several days to find out how to configure the authentication. I found out dozens of different howtos, but none of them worked for me. So when I finally came to one that worked, I created several shell scripts that helped me to configure different kind of (unicast) NTP authentication.
The shell scripts are paired: setup_server_XYZ.sh and setup_client_XYZ.sh where XYZ is used identity scheme. MV identity scheme seems not to work on sles9.The usage of the scripts is:
- Edit PASS/SPASS/CPASS variables in both client and server script! These are password for generated keys
- Edit SERVER variable in setup_server_XYZ.sh - that is the server which will your server use for own synchronization.
- Copy setup_server_XYZ.sh to the ntp server.
- Run setup_server_XYZ.sh on the server. If XYZ is mv, script takes one argument N, which is number of client keys to generate + 1 (N-1 clients will be able to use the server).
- Copy setup_client_XYZ.sh to the client.
- Run setup_client_XYZ.sh
. If XYZ is mv, script takes additional argument X which is client id - number between 1 and N-1 - which specifies which key client will use. The script will ask for server root's password twice - first time to identify the key to download from the server, and the second time to actually download the key. - Make sure that both server and client are not configured to run in chroot (or copy/move /etc/ntp/ and /etc/ntp.conf to appropriate chroot directory).
- Start ntpd (manually or with initscript) on server and clients. It takes some time to establish the link between client and server (approximately 15 minutes).
When ntp is set up and running, the status can be checked with several commands:
- ntpq -p - prints a list of server the host has configured as its NTP servers. If there's a star '*' before the server name, host uses the server for synchronization.
- ntp -c as - print list of associations - including authentication status. Following output shows successfully authenticated association:
ind assID status conf reach auth condition last_event cnt =========================================================== 1 26132 f694 yes yes ok sys.peer reachable 9
The scripts have following limitations:
- Server is configured to use one server - which is not a good idea - it should use more than one (same for clients). To fix this just add more server lines to /etc/ntp.conf.
- Usage of /dev/urandom is not the best one as well, /dev/random (or file generated by command openssl rand) should probably be used for maximal security.
- Configuration is saved to /etc but ntpd usually run in chroot.
- The script setup_server_mv.sh always endlessly loops while generating client keys - seems to continuously reject duplicate keys - this is probably not problem in the script. So, try to use it but don't be surprised ;-)
setup_server_gq.sh:
#!/bin/bash PASS=lala SERVER=ntp2.suse.de rm -fr /etc/ntp mkdir -p /etc/ntp cat <<> /etc/ntp.conf server $SERVER keysdir /etc/ntp crypto pw $PASS randfile /dev/urandom logfile /var/log/ntp logconfig =all EOF cd /etc/ntp RANDFILE=/dev/urandom ntp-keygen -T -G -p $PASS echo server is set up. Start it with \'ntpd -u ntp [-d]\'
setup_client_gq.sh:
#!/bin/bash PASS=lala ping -c1 "$1" > /dev/null if [ $? -ne 0 -o $# -ne 1 ] ; then echo "Needs one argument - ntp server address" exit 1 fi rm -fr /etc/ntp mkdir -p /etc/ntp cat <<> /etc/ntp.conf server $1 autokey crypto pw $PASS randfile /dev/urandom logfile /var/log/ntp logconfig =all keysdir /etc/ntp EOF cd /etc/ntp RANDFILE=/dev/urandom ntp-keygen -H -p $PASS KEY="`ssh root@$1 ls /etc/ntp/ntpkey_GQpar_\* | sed 's,.*/,,'`" if [ "x$KEY" = "x" ] ; then echo Error while fetching key name exit 1 fi scp root@$1:/etc/ntp/$KEY . LINK="`echo $KEY | sed 's/^ntpkey_GQpar_\(.*\)\.[0-9]\+$/ntpkey_gp_\1/'`" ln -s $KEY $LINK echo client is set up. Start it with \'ntpd -u ntp [-d]\'
setup_server_iff.sh:
#!/bin/bash SPASS=lala CPASS=alal SERVER=ntp2.suse.de rm -fr /etc/ntp mkdir -p /etc/ntp cat <<> /etc/ntp.conf server $SERVER keysdir /etc/ntp crypto pw $SPASS randfile /dev/urandom logfile /var/log/ntp logconfig =all EOF cd /etc/ntp RANDFILE=/dev/urandom ntp-keygen -T -I -p $SPASS RANDFILE=/dev/urandom ntp-keygen -e -q $SPASS -p $CPASS > tmp_key echo keyname "'`head -n1 tmp_key | sed 's/^# *//'`'" mv tmp_key `head -n1 tmp_key | sed 's/^# *//'` echo server is set up. Start it with \'ntpd -u ntp [-d]\'
setup_client_iff.sh:
#!/bin/bash CPASS=alal ping -c1 "$1" > /dev/null if [ $? -ne 0 -o $# -ne 1 ] ; then echo "Needs one argument - ntp server address" exit 1 fi rm -fr /etc/ntp mkdir -p /etc/ntp cat <<> /etc/ntp.conf server $1 autokey crypto pw $CPASS randfile /dev/urandom logfile /var/log/ntp logconfig =all keysdir /etc/ntp EOF cd /etc/ntp RANDFILE=/dev/urandom ntp-keygen -H -p $CPASS KEY="`ssh root@$1 ls /etc/ntp/ntpkey_IFFkey_\* | sed 's,.*/,,'`" if [ "x$KEY" = "x" ] ; then echo Error while fetching key name exit 1 fi scp root@$1:/etc/ntp/$KEY . LINK="`echo $KEY | sed 's/^ntpkey_IFFkey_\(.*\)\.[0-9]\+$/ntpkey_iff_\1/'`" ln -s $KEY $LINK echo client is set up. Start it with \'ntpd -u ntp [-d]\'
setup_server_mv.sh (loops!!!):
#!/bin/bash PASS=lala SERVER=ntp2.suse.de if [ "x`echo $1 | grep '^[1-9][0-9]*$'`" = "x" ] ; then echo Needs one argument n - number of client certificates to create + 1. exit 1 fi rm -fr /etc/ntp mkdir -p /etc/ntp cat <<> /etc/ntp.conf server $SERVER keysdir /etc/ntp crypto pw $PASS randfile /dev/urandom logfile /var/log/ntp logconfig =all EOF cd /etc/ntp RANDFILE=/dev/urandom ntp-keygen -V $1 -p $PASS echo server is set up. Start it with \'ntpd -u ntp [-d]\'
setup_client_mv.sh (untested, because setup_server_mv.sh loops!):
#!/bin/bash PASS=lala ping -c1 "$1" > /dev/null if [ $? -ne 0 -o $# -ne 2 -o "x`echo $2 | grep '^[1-9][0-9]*$'`" = "x" ] ; then echo "Needs two arguments:" echo " 1. ntp server address" echo " 2. ntp client id 1..n-1 - which certificate to get." exit 1 fi rm -fr /etc/ntp mkdir -p /etc/ntp cat <<> /etc/ntp.conf server $1 autokey crypto pw $PASS randfile /dev/urandom logfile /var/log/ntp logconfig =all keysdir /etc/ntp EOF cd /etc/ntp RANDFILE=/dev/urandom ntp-keygen -H -p $PASS KEY="`ssh root@$1 ls /etc/ntp/ntpkey_MVkey$2_\* | sed 's,.*/,,'`" if [ "x$KEY" = "x" ] ; then echo Error while fetching key name exit 1 fi scp root@$1:/etc/ntp/$KEY . LINK="`echo $KEY | sed 's/^ntpkey_MVkey[1-9][0-9]*_\(.*\)\.[0-9]\+$/ntpkey_mv_\1/'`" ln -s $KEY $LINK echo client is set up. Start it with \'ntpd -u ntp [-d]\'
That's all, I hope this can spare some time...
(This text has also been published on openSUSE wiki)