2009-12-30

AVR, Gentoo and Paludis

Seeing that there is a new AVR simulator for linux in town, simavr, I decided to get the latest avr gcc packages for Gentoo. (More about simavr when I have gotten a bit accustomed to it)

As the crossdev scripts don't really wants to play nice with Paludis the packages have to be "created" manually. This is really a simple procedure as the packages themself already works for different targets, they just need to be told. I got the information on how to do it from the Gentoo wiki.

First one needs a repo for the cross avr chain. We will call it cross-avr. It is created by putting this configuration file in /etc/paludis/repositories:

master_repository = gentoo
format = ebuild
location = /var/paludis/repositories/cross-avr
names_cache = /var/cache/paludis/names
write_cache = /var/cache/paludis/metadata

And to get the files in the repo, make the directory and create symbolink links for the required packages:

mkdir /var/paludis/repositories/cross-avr/{cross-avr,profiles} -p
cd /var/paludis/repositories/cross-avr/cross-avr
ln -s /usr/portage/dev-embedded/avr-libc
ln -s /usr/portage/sys-devel/binutils
ln -s /usr/portage/sys-devel/gcc
ln -s /usr/portage/sys-devel/gdb

The repo also needs to be named and as the category is not in the master tree (portage) it needs to be added to the categories file:

cd ../profiles
echo cross-avr > repo_name
echo cross-avr > categories

Finally we need to tell paludis to use the avr-gcc for the other cross-avr packages, by placing this in /etc/paludis/bashrc

#cross-build avr
if [[ "${CATEGORY}" == "cross-avr" ]]
then
    if [[ "${PN}" != "gcc" && "${PN}" != "binutils" ]]
    then
        CBUILD=${CHOST}
        CHOST=avr
        CFLAGS="-Os"
    fi
fi

Now we have the ("auto-syncing" :D) repo set up!

To make sure that paludis knows about the new packages we run:
paludis --regenerate-installable-cache cross-avr


Next up is the installation. Do a pretend to see what paludis will pull in:
paludis cross-avr/binutils cross-avr/gcc cross-avr/avr-libc cross-avr/gdb -ip

Here it is important to note all the use flags for gcc. My experience is that most of them will break avr-gcc and my recommendation is to turn them all off.

At last we are ready. Start the build and grab a mug of cocoa.

While you wait it may be interesting to know that avr-libc will place the libs in /usr/avr. And if you want to start playing with simavr you need to change AVR_ROOT, in its Makefile.common, to this path. But more on that another time ;)

When the packages are installed you may want to create a symlink in /usr/avr/lib to /usr/x86_64-pc-linux-gnu/avr/lib/ldscripts (substitute architechture as appropriate) because ld will have problems finding the linker scripts otherwise. (/usr/libexec/gcc/avr/ld: cannot open linker script file ldscripts/avr4.x: No such file or directory, Gentoo bug)

Now you can compile your avr projects with avr-gcc! Happy hacking!

2009-05-29

Custom PATH in KDE run dialog

It has bothered me for a while that the KDE run dialog (Alt+F2) won't find my custom scripts in ~/bin. After a failed attempt, with ~/.kde/env/bin_path, I found a way that should (in theory) work with any desktop manager.

The solution is to put the export command in a file called ~/.xprofile

export PATH="/home/thomas/bin:${PATH}:/home/thomas/installs/bin"

(I keep all my user installed apps in ~/installs/bin ;))

2009-05-22

freeSSHd administrator stupidity, and fix

Searching for a ssh server for a Windows machine I stumbled upon freeSSHd. I thought it looked nice and threw up a remote desktop the Windows machine and installed version 1.2.4.

Everything went fine until I tried to start the administration interface. It told me I need to be an administrator. Well, fine, I am in the administrator group but not the administrator. Tried running the application as the administrator user, no luck.

Going through their forums I found out the user have to be named "Administrator", case and all. Tough luck for us running non-English Windows versions...

As their did not appear to exist a good solution I finally ended up using OllyDbg (great tool, version 2 is under way) to disable the test. I won't post a patched version as I'm pretty sure it is not legal to do so, but I will show what you need to change, either using OllyDbg (yea, it can edit and save the asm directly) or the hex editor of your choice. You probably should make a backup of the original file and stop the freeSSHd service before you start.

Okay, so what you need to do is to replace the JE (jump equal/zero) instruction, that takes us down the wrong road, with two NOPs (no operation). It is at address 00013726 (yea, it's hex, try Ctrl+G in your tool and specify (file) offset), reading 74 49.

Just simply replace those two bytes with 90 90 and save the file.

Voila, it is possible to administrate freeSSHd!



(I would have posted this on the freeSSHd forum if I was able to register :P)

edit.

Seems it starts a new instance when it's run by the "wrong" user. To get around this just stop the service, make the changes, and restart the service (ie from services.msc).

2009-01-23

Yubikey and sv_dvorak

I recently received a yubikey which I immediately started to play with.



To get it to work with a pam authentication module I needed its id. As instructed I tried logging in at the administration site but it kept saying my key was invalid. After some head-scratching I realized why: the yubikey acts as a keyboard and "hits" the keys in the password string. The problem is, I don't use qwerty, so the key was not outputting the characters it thought it did.

So I ended up writing a patch for YubiPAM. It is a bit hackish and I should really have used some sort of dictionary, but at least it works ;)

yubipam-1.0.4-sv_dvorak.patch

--- src/utils/yk_chkpwd.c.old 2008-09-24 09:55:24.000000000 +0200
+++ src/utils/yk_chkpwd.c 2009-01-23 19:15:53.000000000 +0100
@@ -65,6 +65,9 @@

#define MAXPASS 200 /* the maximum length of a password */

+// "abcdefghijklmnopqrstuvwxyz"
+#define DVORAK_MAP "anihdyujgcvpmlsrxo_kf.,bt-"
+
#include <security/_pam_types.h>

int _yubi_verify_password(char *, char *);
@@ -215,6 +218,42 @@
{
pass[npass] = '\0'; /* NUL terminate */
retval = _yubi_verify_password(user, pass);
+
+ if(retval == PAM_CRED_INSUFFICIENT)
+ {
+ // try se_sv_dvorak
+ // password will always get the same size or smaller, because of multibyte chars
+ int y, dy;
+ char c;
+ for(y = 0, dy = 0; (c = pass[y]) != 0; y++, dy++)
+ {
+ // special cases first, we really want to use some sort of dictionary here!
+ if(c == 0xFFFFFFC3) //multibyte
+ {
+ c = pass[++y];
+ switch(c)
+ {
+ case 0xFFFFFFA5: //å
+ pass[dy] = 'q';
+ break;
+ case 0xFFFFFFA4: //ä
+ pass[dy] = 'z';
+ break;
+ }
+ }
+ else if(c == '.')
+ pass[dy] = 'e';
+ else if(c == ',')
+ pass[dy] = 'w';
+ else if(c >= 'a' && c <= 'z')
+ pass[dy] = DVORAK_MAP[c - 'a'];
+ else
+ break; //bail
+ }
+ pass[npass = ++dy] = 0;
+
+ retval = _yubi_verify_password(user, pass);
+ }
}

memset(pass, '\0', MAXPASS); /* clear memory of the password */


As it turns out I started with a patch for yubico-c-client which uses the yubico servers for validation. I switched to YubiPAM when I realized that yubico-pam only supports ssh.

libyubikey-client-1.4-sv_dvorak.patch

Quick 'n' dirty sv_dvorak libyubi with fallback to default layout.

--- libykclient.c.old 2008-09-15 15:27:13.000000000 +0200
+++ libykclient.c 2009-01-21 22:22:40.000000000 +0100
@@ -48,6 +48,9 @@
# define D(x) /* nothing */
#endif

+// "abcdefghijklmnopqrstuvwxyz"
+char* dvorak_map = "anihdyujgcvpmlsrxo_kf.,bt-";
+
struct yubikey_client_st
{
CURL *curl;
@@ -114,11 +117,45 @@
yubikey_client_t p;
int ret;

+ char dvorak_yubikey[100];
+ char c;
+ int y;
+ int dy;
+ for(y = 0, dy = 0; (c = yubikey[y]) != 0; y++, dy++)
+ {
+ // special cases first, we really want to use some sort of dictionary here!
+ if(c == 0xFFFFFFC3) //multibyte
+ {
+ c = yubikey[++y];
+ switch(c)
+ {
+ case 0xFFFFFFA5: //å
+ dvorak_yubikey[dy] = 'q';
+ break;
+ case 0xFFFFFFA4: //ä
+ dvorak_yubikey[dy] = 'z';
+ break;
+ }
+ }
+ if(c == '.')
+ dvorak_yubikey[dy] = 'e';
+ else if(c == ',')
+ dvorak_yubikey[dy] = 'w';
+ else if(c >= 'a' && c <= 'z')
+ dvorak_yubikey[dy] = dvorak_map[c - 'a'];
+ else
+ break; //bail
+ }
+ dvorak_yubikey[++dy] = 0;
+
p = yubikey_client_init ();

yubikey_client_set_info (p, client_id, keylen, key);

- ret = yubikey_client_request (p, yubikey);
+ ret = yubikey_client_request (p, dvorak_yubikey); //sv_dvorak
+ if(ret == YUBIKEY_CLIENT_BAD_OTP)
+ ret = yubikey_client_request(p, yubikey); //qwerty
+

yubikey_client_done (&p);

2008-12-30

Pavucontrol sink name patch

With the number of sinks I have accumulated over time using pavucontrol has become more confusing. Therefore I checked out the git source and patched pavucontrol to show the names of my sinks and sources.

After writing the patch I got the idea to check the bug tracker and there was already a patch, although I prefer my formatting of the labels :P.

I splitted my patch into two, as the second one is kinda ugly :) (does not handle the updating flag correctly)


diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc
index b7f7a9b..d79f068 100644
--- a/src/pavucontrol.cc
+++ b/src/pavucontrol.cc
@@ -794,7 +794,7 @@ void SinkInputWidget::clearMenu() {
void SinkInputWidget::buildMenu() {
for (std::map::iterator i = mainWindow->sinkWidgets.begin(); i != mainWindow->sinkWidgets.end(); ++i) {
SinkMenuItem *m;
- sinkMenuItems[i->second->index] = m = new SinkMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sinkIndex);
+ sinkMenuItems[i->second->index] = m = new SinkMenuItem(this, (i->second->name + " (" + i->second->description + ")").c_str(), i->second->index, i->second->index == sinkIndex);
submenu.append(m->menuItem);
}

@@ -1033,7 +1033,7 @@ void MainWindow::updateSink(const pa_sink_info &info) {

w->boldNameLabel->set_text("");
gchar *txt;
- w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
+ w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s (%s)", info.name, info.description));
g_free(txt);

w->iconImage->set_from_icon_name("audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR);
@@ -1175,7 +1175,7 @@ void MainWindow::updateSource(const pa_source_info &info) {

w->boldNameLabel->set_text("");
gchar *txt;
- w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
+ w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s (%s)", info.name, info.description));
g_free(txt);

w->iconImage->set_from_icon_name("audio-input-microphone", Gtk::ICON_SIZE_SMALL_TOOLBAR);





diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc
index d79f068..69dba03 100644
--- a/src/pavucontrol.cc
+++ b/src/pavucontrol.cc
@@ -1232,6 +1232,14 @@ finish:
icon->set_from_icon_name(t, Gtk::ICON_SIZE_SMALL_TOOLBAR);
}

+void sinkName_cb(pa_context *, const pa_module_info *i, int eol, void *userdata) {
+ if(i == NULL) {
+ return;
+ }
+ SinkInputWidget *w = static_cast(userdata);
+ w->nameLabel->set_label(g_markup_printf_escaped("%s (%s)", i->name, w->nameLabel->get_label().c_str()));
+}
+
void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
SinkInputWidget *w;
bool is_new = false;
@@ -1265,7 +1273,11 @@ void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
g_free(txt);
} else {
w->boldNameLabel->set_text("");
- w->nameLabel->set_label(info.name);
+ w->nameLabel->set_label(info.name);
+ if(info.owner_module != PA_INVALID_INDEX) {
+ g_debug(_("num: %i"), info.owner_module);
+ pa_context_get_module_info(context, info.owner_module, sinkName_cb, w);
+ }
}

setIconFromProplist(w->iconImage, info.proplist, "audio-card");
@@ -1311,8 +1323,12 @@ void MainWindow::updateSourceOutput(const pa_source_output_info &info) {
w->nameLabel->set_markup(txt = g_markup_printf_escaped(": %s", info.name));
g_free(txt);
} else {
- w->boldNameLabel->set_text("");
- w->nameLabel->set_label(info.name);
+ w->boldNameLabel->set_text("");
+ w->nameLabel->set_label(info.name);
+ if(info.owner_module != PA_INVALID_INDEX) {
+ g_debug(_("num: %i"), info.owner_module);
+ pa_context_get_module_info(context, info.owner_module, sinkName_cb, w);
+ }
}

setIconFromProplist(w->iconImage, info.proplist, "audio-input-microphone");


And here's how it looks:

2008-12-22

Solving the mono problem

In an earlier post I showed you my sink-remapping which sent audio either to my speakers or my headphones. The problem with this setup is that I needed to disable remixing, effectively muting all mono sounds (and probably screw up surround sound, haven't tried that). The solution, or rather fix, is as follows:


#mono mappings
load-module module-remap-sink sink_name=mono-spk master=sound_card channels=2 master_channel_map=front-left,front-right channel_map=mono,mono
load-module module-remap-sink sink_name=mono-head master=sound_card channels=2 master_channel_map=rear-left,rear-right channel_map=mono,mono


This lets me hear audio from mono sources by sending them to these upmixing sinks.

The two drawbacks with this solution is that I have to switch sink manually and applications playing both mono and stereo will still be muted for either mono or stereo (although I haven't used any such application)

I did try to remix the sinks without success:

load-module module-combine sink_name=speakers-comb slaves=speakers,mono-spk channels=2 channel_map=front-left,front-right

If anyone got this working, please tell me how :)

2008-11-29

Suspending usb mouse on screensaver

Having one of the smaller desks in the universe I often encounter trouble when trying to do my homework without accidentally moving the mouse. Also sometimes I get disturbed by its red light when trying to sleep. Therefore I decided that my mouse should be turned off when the screensaver starts. And here's how to do it:

First open up your kernel config and make sure CONFIG_USB_SUSPEND (USB selective suspend/resume and wakeup) is enabled. Rebuild and reboot as necessary.

Now we are ready to start playing with the mouse. Run lsusb to find the proper bus and device id, here's the output from my system: (to anyone reading this through rss/planet lysator I recommend reading the original post if you want nice code formatting)

$ lsusb
Bus 002 Device 004: ID 046d:c30e Logitech, Inc.
Bus 002 Device 003: ID 0424:2504 Standard Microsystems Corp. USB 2.0 Hub
Bus 002 Device 001: ID 1d6b:0002
Bus 008 Device 001: ID 1d6b:0001
Bus 007 Device 001: ID 1d6b:0001
Bus 006 Device 001: ID 1d6b:0001
Bus 005 Device 002: ID 045e:0095 Microsoft Corp.
Bus 005 Device 001: ID 1d6b:0001
Bus 004 Device 001: ID 1d6b:0001
Bus 001 Device 001: ID 1d6b:0002
Bus 003 Device 001: ID 1d6b:0001


Unfortunately I have not found a way to use the device name to identify it, so it has to be done by bus and device numbers. I have a Microsoft mouse so I notice the numbers and go looking under /sys/bus/usb/devices/5-2/power/ (bus 5, dev 2). (The numbering can get more interesting if your device is connected through a hub, then you have to start guessing the correct directory and cat busnum and devnum respectively)

$ ls /sys/bus/usb/devices/5-2/power
active_duration autosuspend connected_duration level persist wakeup


The important "file" for our purpose is the one named level. But before continuing one should also note wakeup (tell the kernel whether the device should be able to wake itself up or not) and persist (really cool feature: if a device gets disconnected and the connected again this will make it continue running as it were never disconnected. Really nifty when one is hibernating with mounted usb disks and such.) (More info: /usr/src/linux/Documentation/usb/{power-management,persist}.txt)

Returning to level, this "file" has three states: on, auto and suspend. Setting level to on will force the device to never suspend, setting it to auto will automatically suspend it after autosuspend seconds (at least in theory, does not seem to work for me (maybe my mouse is never idle/the driver does not support it?)) and suspend will suspend the device (it will still have some power to be able to wake up and for the system to know if it is still there (yep, I lost the sources)).

Make sure you have found the correct device (it isn't equally funny to suspend your keyboard ;)) and try the following: (replacing the bus and dev num for the ones your mouse have)

$ cat /sys/bus/usb/devices/5-2/power/level
auto
# sudo -s
# echo suspend > /sys/bus/usb/devices/5-2/power/level
# echo auto > /sys/bus/usb/devices/5-2/power/level

This should tell you which state your mouse initially is in, suspend the mouse (LEDs/laser turns off, try to move it ;)) and finally turn the mouse back to the original state (if it was auto). (Try echoing enabled/disabled to wakeup and click the mouse while it is suspended)

Having got all that working it is time to set up the automatics: First we need a way for a user to turn the mouse on/off (okay, we don't really need that but I find it nicer to run as much as possible as a normal user ;)).

With my small c skills and google I threw this together:
suspend_mouse.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
FILE *file = fopen("/sys/bus/usb/devices/5-2/power/level", "w");

if(file == NULL)
{
fprintf(stderr,"Can not open device file for writing, are you root?\n");
exit(1);
}

if(argc == 1 || strcmp("1", argv[1]) == 0)
fputs("suspend", file); //check error
else if(argc > 1 && strcmp("0", argv[1]) == 0)
fputs("auto", file);
fclose(file); //try-catch?

return 0;
}

Makefile

default:
gcc suspend_mouse.c -o suspend_mouse


Compile it by running

$ make
gcc suspend_mouse.c -o suspend_mouse


Copy it to /usr/bin and setuid it so all users can turn off the mouse (maybe not the safest way :)) (oh, and here is why you shouldn't do this in sh/bash: How can I get setuid shell scripts to work?)

$ sudo cp suspend_mouse /usr/bin
$ sudo chmod +sx /usr/bin/suspend_mouse
$ ls -l /usr/bin/suspend_mouse
-rwsr-sr-x 1 root root 11K 2008-11-29 18:14 /usr/bin/suspend_mouse


And try it out

$ suspend_mouse
$ suspend_mouse 1
$ suspend_mouse 0
$ suspend_mouse 1


There we are, almost finished. Now we just need the mouse to suspend when the screensaver is running. Poking around a bit with qdbus gave me ActiveChanged under org.freedesktop.ScreenSaver. Picking up and modifying one of my earlier dbus experiments this is the result in python:
screensaver_dbus.py

#!/usr/bin/env python

import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
import os

def on_activechanged(active):
if(active):
os.system('/usr/bin/suspend_mouse 1')
else:
os.system('/usr/bin/suspend_mouse 0')

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

screensaver = bus.get_object("org.kde.screensaver", "/ScreenSaver")

bus.add_signal_receiver(on_activechanged,
dbus_interface="org.freedesktop.ScreenSaver",
signal_name="ActiveChanged")

loop = gobject.MainLoop()
loop.run()

This script basically registers itself for the ActiveChanged signal and the goes into a dbus listening loop and reacts every time the screensaver is turned on or off. It should preferably be started when logging in.

And we are finished!

Two seconds after writing the code I realized that I don't use the screensaver and I haven't found a way to detect when DPMS is turned on/off by means of using dbus. Any information on this is welcome ;) Also one would maybe want to make this more global and not depending on the current user session.