Force zoomed workspaces in GNOME 3.2

October 7, 2011 Comments off

I jumped the gun and updated to Fedora 16 in beta, which comes with GNOME 3.2 as well.  With that comes a tweaked extension interface, so my prior hack needs modification.  I do notice the new behavior that Florian mentioned last time, where the workspaces display now only hides if just one workspace is in use.  So my previous annoyance is not so bad anymore, but I’ve grown accustomed having it always-zoomed, no matter what.  Same as before, put these in ~/.local/share/gnome-shell/extensions/zoomWorkspacesDisplay@cuviper.com/:

metadata.json :

{
    "shell-version": ["3.2"],
    "uuid": "zoomWorkspacesDisplay@cuviper.com",
    "name": "Zoom Workspaces Display",
    "description": "Forces the workspaces display to always be zoomed",
    "url": "https://blog.cuviper.com/",
    "original-authors": "Josh Stone"
}

extension.js :

const Overview = imports.ui.main.overview;
const WorkspacesView = imports.ui.workspacesView;

function init(metadata) {}

let _saved_updateAlwaysZoom;

function enable() {
    let p = WorkspacesView.WorkspacesDisplay.prototype;
    _saved_updateAlwaysZoom = p._updateAlwaysZoom;
    p._updateAlwaysZoom = function() {
        this._alwaysZoomOut = true;
    };
    Overview._workspacesDisplay._alwaysZoomOut = true;
}

function disable() {
    let p = WorkspacesView.WorkspacesDisplay.prototype;
    p._updateAlwaysZoom = _saved_updateAlwaysZoom;
    Overview._workspacesDisplay._alwaysZoomOut = false;
    Overview._workspacesDisplay._updateAlwaysZoom();
}

I’m throwing it up on github too. If I need any more tweaks, I’ll just push there rather than blogging about it.

Dnsmasq fixing Comcast and VPNs

July 23, 2011 Comments off

Twice in the last few weeks I’ve found uses of dnsmasq to solve DNS headaches: first to get around Comcast issues, and second to juggle both VPN and local hostnames.

Round 1 vs. Comcast:  I discovered a while ago that I couldn’t get DNS resolution of a particular domain, elastic.org, nor its subdomains, though it would resolve just fine using a third-party DNS like Google’s 8.8.8.8.  I couldn’t find any reason for this failure, the site’s admin had no idea, and of course Comcast support was no help.  Surprise, rebooting my computer doesn’t help, when I just told you that every Windows and Linux computer I have fails even querying the server directly!

I hesitate to use Google DNS permanently, mainly for fear of losing CDN benefits, so for a while I just hard-coded the addresses in my hosts files.  That’s not ideal in case they ever change, but the web is a fairly static place in practice.  But then I discovered that dnsmasq has an option to route specific domains to specific DNS servers.  My router is running DD-WRT with dnsmasq, so I just had to add “server=/elastic.org/8.8.8.8” to the config, and that one troublesome domain can now query through Google while the rest go through Comcast.  That’s still a bit of a headache to maintain as I discovered other domains with a similar issue, but the problem was somewhat solved.

Round 2 vs. Comcast: Recently I also started having outright connection issues, which turned out to be dropping due to too-high transmit power negotiated in the modem.  They replaced wiring to improve this, but in the process of troubleshooting they also “reactivated” my account.  I noticed that the router was now getting different DNS servers from DHCP, so I tried elastic.org again and it worked!  I manually queried both the old and new servers, and sure enough only the new was able to resolve that domain.

Turns out that the different servers came because the reactivation had also reset the Domain Helper feature, which I had previously opted-out of.  The DNS that DHCP gives you depends on your choice in this setting.  When the “helper” DNS is asked about an unknown name, it will give a bogus resolution to 208.68.143.50, which is a Comcast web server that informs you of the failure and does a web search instead.  This is sort of OK for regular HTTP traffic, but it’s broken for many other uses.  I’d rather have the proper NXDOMAIN, so the browser and any other clients can act accordingly.  Dnsmasq comes to the rescue again with the option “bogus-nxdomain=208.68.143.50” in my config, and it will convert any name resolved to that address to a real NXDOMAIN instead.  So faced with two broken Comcast DNS options, either unable to resolve good names or improperly resolving bad names, I’m able to eke out full and proper functionality again – yay!

[Update Aug 5, 2011] I just got another search page, argh!  Turns out the IP I gave above is for search5.comcast.com, and this time I got search3.comcast.com, so this may be a bit of whack-a-mole.  I scanned search[0-9] now, and got these three to set bogus-nxdomain: 207.223.0.140, 208.68.139.38, and 208.68.143.50.

Bonus round vs. VPN: I have several machines on my home network, and the router’s dnsmasq already does a fine job of resolving those local names.  But my work computer uses openvpn to connect to the corporate network, and for that it needs the VPN’s DNS servers so it can resolve intranet names.  I really want it to resolve names in both networks though.  Then I discovered that NetworkManager has a configuration option “dns=dnsmasq“, which causes it to start a local dnsmasq with a “server=” option for the VPN’s domain.  This means that only requests for the corporate domain will go through the VPN, and all others will use the local connection’s DNS, including the general internet and all my home machines.  Perfect!

Tags: , ,

Force zoomed GNOME 3 workspaces

July 10, 2011 4 comments

Almost a month ago now, I upgraded to Fedora 15, which brings with it GNOME 3.  The UI changes are huge — oh, how the webs are screaming.  One of the biggest issues is how very little is customizable.  “How dare you question the brilliant designers!?!  Surely they know what works best for you!”  But it does have an extension system, so one can write in additional functionality.  For adding features, this looks reasonable, but if you want to change features, get ready to monkey-patch into undocumented (and probably unstable) interfaces.

Well, there are plenty of longer rants out there, so I’ve said enough.  When it actually comes down to it, I’m getting used to the changes, and it turns out that the shell doesn’t really matter so much.  I don’t really feel like GNOME 3 is helping me be more productive, but it’s not hindering me either… faint praise.

I have identified a particular pain point though, so I decided I should share how I monkey-patched a solution.  When you go into overview mode, there’s a partially hidden bar on the right side that displays your workspaces.  Mouse over that, and it expands into view for you to choose your destination.

So far, so good — if that right side is hard edge for your mouse, then it’s easy to snap over there.  However, if it’s not a hard edge, as with multiple monitors, then the hidden view is a difficult target.  The gnome-shell developers did think of this, so they check the monitor positions:

js/ui/workspacesView.js :

    _updateAlwaysZoom: function()  {
        this._alwaysZoomOut = false;

        let monitors = global.get_monitors();
        let primary = global.get_primary_monitor();

        /* Look for any monitor to the right of the primary, if there is
         * one, we always keep zoom out, otherwise its hard to reach
         * the thumbnail area without passing into the next monitor. */
        for (let i = 0; i < monitors.length; i++) {
            if (monitors[i].x >= primary.x + primary.width) {
                this._alwaysZoomOut = true;
                break;
            }
        }
    },

My issue is that I have an additional monitor on the right edge that GNOME 3 knows nothing about, thanks to Synergy. So I want to just force that _alwaysZoomOut = true, since in this case I really do know better. Thankfully, javascript is flexible enough that I can just replace that whole function with my forced values. Thus an extension is born:

metadata.json :

{
    "shell-version": ["3.0.2"],
    "uuid": "zoomWorkspacesDisplay@cuviper.com",
    "name": "Zoom Workspaces Display",
    "description": "Forces the workspaces display to always be zoomed",
    "url": "https://blog.cuviper.com/",
    "original-authors": "Josh Stone"
}

extension.js :

const Overview = imports.ui.main.overview;
const WorkspacesView = imports.ui.workspacesView;

function main() {
    WorkspacesView.WorkspacesDisplay.prototype._updateAlwaysZoom = function() {
        this._alwaysZoomOut = true;
    };
    Overview._workspacesDisplay._alwaysZoomOut = true;
}

Put those in ~/.local/share/gnome-shell/extensions/zoomWorkspacesDisplay@cuviper.com/, and reload the shell (Alt-F2, r). Now the workspace chooser will always be fully displayed whenever you’re in the overview.

Update: Florian commented that he’s already improved the way this is handled for the next version of gnome-shell.  For my own interest, I tracked it down to this bug and commit.  Thanks!

pdiff: Comparing process output

March 5, 2011 Comments off

The diff command is an essential piece of any programmer’s or administrator’s toolbox, to show the difference between two files.  Or if not that tool exactly, there are many like it.  But sometimes I want to compare the output of two commands, often really the same command operating on different inputs.  You could save the output of each to separate files and diff that, or in a shell like bash you can use “<(cmd)” fed directly to diff.  For example, when I was recently dealing with an elfutils bug with prelinked binaries, I ran a command like this:

diff <(eu-readelf -hlS foo) <(eu-readelf -hlS foo.prelink)

That works just fine, though it’s a little redundant, but it gets really bad if the command gets much more complex.  And if you need to adjust the command, you have to do it in both places, or the diff falls apart.  So after getting annoyed by this, I decided to scratch that itch and write this “pdiff” script:

#!/bin/bash

CMD=
CMDARG=
DIFF=
OPTS=$(getopt -o d:gvc:I: -n pdiff -- "$@") || exit
eval set -- "$OPTS"
while true; do
    case "$1" in
        -d) DIFF="$2"; shift 2;;
        -g) DIFF=gvimdiff; shift;;
        -v) DIFF=vimdiff; shift;;
        -c) CMD="$2"; shift 2;;
        -I) CMDARG="$2"; shift 2;;
        --) shift; break;;
         *) echo "Internal error!" >&2; exit 2 ;;
    esac
done

function run() {
    local cmd
    test -n "$CMDARG" && cmd="${CMD/$CMDARG/$@}" || cmd="$CMD $@"
    echo "# $cmd"
    eval $cmd
}

case $# in
    2) exec ${DIFF:=diff -u -p} <(run $1) <(run $2);;
    3) exec ${DIFF:=diff3} <(run $1) <(run $2) <(run $3);;
    4) test -n "$DIFF" &&
       exec $DIFF <(run $1) <(run $2) <(run $3) <(run $4);;&
    *) echo "Unable to diff $# args" >&2; exit 1 ;;
esac

It even has option processing, like -v to open in Vim, -g for gVim, or -d for a custom diff command.  Then -c gives a shared command, and -I lets you specify a replacement string within the command (rather than placing arguments at the end).  Any remaining arguments get fed to separate invocations of the command for comparison.  That’s well beyond the one-liner I started with, but now I can reuse this to make future comparisons easier.  The first command I gave is now more simply:

pdiff -c 'eu-readelf -hlS' foo foo.prelink

That command can now get more complex, with compound pipelines or whatever else I need, without being such a pain to compose.

I did a little searching afterward and found that there are already other tools called pdiff, like here and here, so if I ever want to publish this more formally I should find a new name.  But for now, it meets my needs, and maybe it can be useful for someone else too.

ssh-pageant 1.0

September 21, 2010 Comments off

ssh-pageant now has its first release, version 1.0 — fulfill your authentication agent needs between Cygwin’s OpenSSH and Pageant today!  It’s documented, and pre-built binaries are available.  And there will be cake, of course.

Any “1.0” feels like it must have some magical aura, but in the end it is just a number.  The program works as expected, and has the features I felt were needed to be complete, so 1.0 it is.  The next step is to get this packaged and included in the official Cygwin repositories, which I would welcome volunteer help in doing.

Two tiny additions to FOSS

August 21, 2010 Comments off

The quietness around here shows that I’m not much of a blogger.  That’s ok, I guess — it’s not like I wanted to make a living from it.  Well anyway, I’ve published a couple of tools I wrote a while ago, and they deserve at least a tiny announcement.  So here are two new contributions from me to the FOSS world.

ssh-pageant is an SSH authentication agent for Cygwin that links OpenSSH to PuTTY’s Pageant.  It acts very much like ssh-agent, except it leaves the key storage to Pageant.  You can use ssh-pageant to automate SSH connections from the Cygwin shell, and I find this is most helpful for those services built on top of SSH, like SFTP file transfers or pushing to secure git repositories.  It is even said to be literally life-saving.

MouseWinX is a Windows tray application that lets you quickly toggle X-mouse window activation, where the window focus follows position of the mouse cursor without having to click anything.  I wrote this back in college when I was using Magic VLSI for one of my classes, and I’ve also found it helpful for navigating GIMP.  Both are applications with multiple windows to interact with, and hot-tracking the mouse makes them a bit easier to use.  But the rest of the time I don’t like having the window focus jump around, so MouseWinX gives a simple icon in the tray which toggles the setting when clicked.

Both of these projects are in an unpolished state, like so many open source projects.  They work perfectly well, as far as I know, but I haven’t done the finishing touches, like writing documentation and packaging releases.  For now, at least the source code is out there, and hopefully this post will help them be found by those in need of such tools.

Hacking Linux Filenames

April 8, 2009 2 comments

I recently read an LWN article on David A. Wheeler’s essay, “Fixing Unix/Linux/POSIX Filenames.”  The gist is that he thinks the filename rules are too permissive — we have ‘/’ as the path separator, and a raw 0 terminates the path, but anything else is fair game.  On the surface, this has a certain beautiful simplicity to it.  However, there are characters that have special meaning depending on the context, so almost any code that actually tries to interpret a filename will have to add a lot of complexity to be robust.  The essay delves into many ways that things can go wrong.

Filenames have been this way in for a long time though, and I don’t expect that this will change officially anytime soon.  Still, my day job now is developing SystemTap, and this sort of problem is one of many sorts that SystemTap can address.  Here’s a script to show how a system administrator could patch the kernel with their own addendum to the filename rules:

#!/usr/bin/stap -g
# badname.stp
# Prevent the creation of files with undesirable names.

# return non-zero if the filename should be blocked
function filter:long (name:string)
{
  return euid() && isinstr(name, "XXX")
}

global squash_inode_permission
probe kernel.function("may_create@fs/namei.c")
{
  # screen out the conditions which may_create will fail anyway
  if ($child->d_inode || $dir->i_flags & 16) next

  # check that the new file meets our naming rules
  if (filter(kernel_string($child->d_name->name)))
    squash_inode_permission[tid()] = 1
}
probe kernel.function("inode_permission@fs/namei.c").return !,
      kernel.function("permission@fs/namei.c").return
{
  if (!$return && squash_inode_permission[tid()])
    $return = -13 # -EACCES (Permission denied)
  delete squash_inode_permission[tid()]
}

The script starts by defining a filter function.  It first check whether the effective user ID is non-zero, so the root user can bypass the filter.  Then, for the prude admins out there, I’ve chosen to block filenames that contain the string “XXX”.  I intentionally kept this part small for this example, but you could easily write a function covering all of the new rules that Wheeler suggests.

After that is a probe on the may_create function, which is what the kernel calls to validate permissions for new files.  We can call our filtering function from here to see if the filename is OK, but since may_create is an inline, we don’t have a direct way to influence its result.  The last thing may_create does though is copy the result of inode_permission (or permission in earlier kernels), which we can override.  So, we save the filtering decision in a global, and then in a return probe on inode_permission, we can change the successful $return code to our own error value.  Now, any attempt to create a file that doesn’t pass our rules will get an error of “Permission denied”.

This sort of script is really just a band-aid, and it doesn’t do anything to deal with files that already have “bad” names.  Still, I hope this is an interesting example of how easily one can modify kernel behavior with SystemTap.  This script can be a starting point to define and try out your own filename rules, and changes can be reloaded and tested without ever having to reboot.  Once your policy has been decided, you can configure the script to load as soon as the system boots, so you’re always running with your improved filename rules, even across kernel upgrades.

It’s powerful stuff, but don’t let it get to your head… 🙂

%d bloggers like this: