Elmord's Magic Valley

Computers, languages, and computer languages. Às vezes em Português, sometimes in English.

Posts com a tag: unix

Honey, I moved ld.so (or, How to recover your system if you moved /usr or /lib)

2024-05-26 23:35 +0100. Tags: comp, unix, in-english

Suppose, not entirely hypothetically, that you move /usr to /usr_old in a running Linux system. If you are using a modern system in which /bin, /lib, etc. are symlinks to /usr/bin, /usr/lib, etc., you will find out that no commands work anymore, including mv, which means you cannot undo the mess:

root@cursed:/# mv /usr /usr_old
root@cursed:/# ls
bash: ls: command not found
root@cursed:/# mv /usr_old /usr
bash: /usr/bin/mv: No such file or directory

So far, not surprising, because it’s still looking up the commands in the old path. But if you update PATH, it still doesn’t work:

root@cursed:/# PATH=/usr_old/bin
root@cursed:/# mv /usr_old /usr
bash: /usr_old/bin/mv: No such file or directory

bash finds mv in the new path (/usr_old/bin/mv), but says the file doesn’t exist. In fact, even if you call the executable by its full path, you will get the same error, even though the file is there:

root@cursed:/# /usr_old/bin/mv
bash: /usr_old/bin/mv: No such file or directory

What gives?

If you’re using a recent enough version of bash (5.2+), you will get a slightly less mystifying message:

root@cursed:/# /usr_old/bin/mv
bash: /usr_old/bin/mv: cannot execute: required file not found

So the problem is not that /usr_old/bin/mv is missing, but some required file. But what required file?

If you just want the solution, you can just jump to the end of the post. In the rest of this post, we are going to do a deep dive into what is going on here.

Understanding the error

Let’s go back to the older bash error message:

bash: /usr_old/bin/mv: No such file or directory

Why is bash saying that the file does not exist? To understand this one, we need to know a bit about how error handling works in C. C does not have exceptions: when a function fails, it typically signals the error by returning an error value, which depends on the specific function. A lot of standard library / POSIX functions indicate errors by returning -1 and setting a global errno variable with one of various constants indicating which specific error happened. In the errno manpage, we can see a list of possible error constants; one of these is ENOENT, whose meaning is “No such file or directory”. (The name ENOENT comes from “error: no entry”, i.e., no such entry in the directory when looking up a file name.) There is also a function strerror which, given an error constant, returns a string corresponding to the error. So, for example, strerror(ENOENT) returns the string No such file or directory (or possibly a locale-appropriate equivalent). Programs often use this function (or similar ones such as perror) to report errors to the user. So if a program says No such file or directory, it’s quite likely some system call returned ENOENT to it.

There are two things to keep in mind here, though. First, the error constant does not carry any information about which file was not found: if a system call fails with ENOENT, all we know is that it hit a “no such file/directory/entry” error during its execution. Only context can tell us what file it might refer to. Second, even though the error constants have more or less standardized meanings, specific system calls can give more specific meanings to these constants. Each system call’s manpage specifies in what circumstances each error constant is produced. To figure out what ENOENT really means in a given situation, we need to know which system call produced it. So, which system call was bash calling? And why the message changed in bash 5.2?

We can figure that out by hunting down the message in bash’s git history. The added bit is:

+      else if (i == ENOENT)
+       {
+         errno = i;
+         internal_error (_("%s: cannot execute: required file not found"), command);
+       }

If we look further up in this file, we will see that i is set to the value of errno after calling execve, a system call to execute a program. bash is using it to try to execute /usr_old/bin/mv, but it’s getting an ENOENT back, so it prints the required file not found error.

Before bash 5.2, it did not handle ENOENT specially here, and instead the code fell through a more generic error handling path further down in this function, which calls file_error, which uses strerror to generate an error message, which is why we ended up seeing No such file or directory. So at least that part makes sense.

Now we need to figure out why execve is giving an ENOENT. If we look at execve’s manpage, we see that, for this specific system call, an ENOENT means:

The file pathname or a script or ELF interpreter does not exist.

That’s interesting. execve fails with ENOENT not only when the file to be executed does not exist (which is not our case), but also when the ELF interpreter does not exist. So…

What the hell is an ELF interpreter?

ELF is the binary format used for executables in Linux and various other Unix-like systems. Most Linux executables are dynamically linked: they are not standalone executables, but rather they depend on system libraries that need to be loaded at runtime for the program to work. Most programs depend at least on libc (the standard C library), and usually on a bunch of others as well. These libraries have to be loaded and linked to the main program (i.e., references from the main program to library functions and variables have to be adjusted to point to the places in memory where the library was actually loaded at runtime) before it starts to run.

The way this works is by having another program, the dynamic linker, called ld.so or ld-linux.so, do the job of loading the main program and the libraries, linking them together, and then starting the main program. So when you call an executable like mv, what actually gets executed first is ld-linux.so, which loads mv, figures out which libraries it depends on, loads those libraries, links everything together, and then passes the control to mv.

And how does the system know that to run mv it has to call ld-linux.so first? Well, the mv executable (and every other executable that uses the dynamic linker) has ld-linux.so as its ELF interpreter. You know how a shell script specifies an interpreter by having #!/path/to/interpreter in its first line, and when you invoke the script, that program gets called to process the script? Well, an ELF file can also specify an interpreter, by embedding the path to the interpreter as a section of the ELF file. If you run file on mv (in a non-broken system), you will see something like:

$ file /bin/mv
/bin/mv: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=24ef388d6e73508a1be274e260bbe654edb327be, for GNU/Linux 3.2.0, stripped

Here we see that its interpreter is /lib64/ld-linux-x86-64.so.2, which is what will get called when you invoke mv. (The process is more convoluted than a regular shell script interpreter invocation, which means you can’t use any random program as an ELF interpreter, but the idea is similar.)

Now, /lib64/ld-linux-x86-64.so.2 is a symlink to /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2, and /lib is a symlink to /usr/lib, so the system is looking for /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2, which is missing because we moved /usr. That is the missing file!

But just like we can invoke a script either directly by its name or by invoking its interpreter explicitly passing the path to the script as an argument, it turns out we can also call ld-linux.so explicitly passing our executable as an argument:

root@cursed:/# /usr_old/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /usr_old/bin/mv
/usr_old/bin/mv: error while loading shared libraries: libselinux.so.1: cannot open shared object file: No such file or directory

Well, we’re not quite there yet, but that’s progress: it found the linker (because we called it explicitly), but now the linker is failing because it cannot find the libraries mv depends on (since we also moved them). But if you call /usr_old/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 --help, you can see that the linker supports a bunch of options, one of which is --library-path, which we can use to point it to the new path of the dynamic libraries.

The solution

The solution, then, is to invoke the linker manually and provide the modified library path explicitly:

root@cursed:/# /usr_old/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 \
    --library-path /usr_old/lib/x86_64-linux-gnu/ \
    /usr_old/bin/mv /usr_old /usr

And now everything works again:

root@cursed:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

Comentários / Comments

Fixing audio device priorities in PulseAudio

2023-10-31 16:45 +0000. Tags: comp, unix, audio, in-english

Ever since I started using my current laptop (a ThinkPad E14 Gen 4, currently running Debian 12), I have had the following issue: when I connect headphones to the laptop, the audio goes to the headphones as expected, but when I disconnect the headphones, the audio goes to the HDMI output instead of going back to the laptop speakers. As it happens, my external monitor (an Eizo FlexScan EV2360) has audio output, but it’s pretty low-quality and I don’t use it. But PulseAudio (or is it ALSA?) assigns higher priority to the HDMI outputs, so as soon as the headphones are disconnected, it looks for the available device with the highest priority and picks the HDMI one. You can see the priority of each audio sink (output) by using pactl:

$ pactl list sinks | grep priority:
        [Out] HDMI3: HDMI / DisplayPort 3 Output (type: HDMI, priority: 700, not available)
        [Out] HDMI2: HDMI / DisplayPort 2 Output (type: HDMI, priority: 600, not available)
        [Out] HDMI1: HDMI / DisplayPort 1 Output (type: HDMI, priority: 500, available)
        [Out] Speaker: Speaker (type: Speaker, priority: 100, availability unknown)
        [Out] Headphones: Headphones (type: Headphones, priority: 200, not available)

In this case, the HDMI1 output is available and has priority 500, whereas the speakers have priority 100.

The solution is to change the HDMI priorities. The problem is where to set this. In my particular case, this was set in /usr/share/alsa/ucm2/Intel/sof-hda-dsp/Hdmi.conf (included from /usr/share/alsa/ucm2/Intel/sof-hda-dsp/Hdmi.conf), which looks like this:

# Use case Configuration for sof-hda-dsp

Include.hdmi.File "/codecs/hda/hdmi.conf"

If.hdmi1 {
    Condition { Type AlwaysTrue }
    True.Macro.hdmi1.HDMI {
        Number 1
        Device 3
        Priority 500

If.hdmi2 {
    Condition { Type AlwaysTrue }
    True.Macro.hdmi1.HDMI {
        Number 2
        Device 4
        Priority 600

If.hdmi3 {
    Condition { Type AlwaysTrue }
    True.Macro.hdmi1.HDMI {
        Number 3
        Device 5
        Priority 700

I just changed the values 500, 600, 700 manually to 50, 60, 70 in this file. This is not a very good solution because this file belongs to the alsa-ucm-conf package and will get overridden whenever I upgrade it, but since this is Debian stable, I don’t have to worry about this any time soon. There is probably a better way to override these values, but I don’t know enough about either ALSA or PulseAudio (and I’m not particularly keen on learning more, unless my fellow readers know and want to leave a helpful comment), so this will have to do for now.

* * *

Another recurring issue is getting Bluetooth headphones to become the selected default device when they are connected. It seems that Bluetooth devices get created dynamically with a bunch of hardcoded priorities (and worse, sometimes I get a device with priority 40 and sometimes 0, I don’t know why). But it also seems that the priorities just don’t have any effect on the selection of the Bluetooth device. I was having a curious issue where some programs would pick the headphones and some wouldn’t, and the default device would remain unchanged (which among other things meant that my multimedia keys set the volume of the wrong device). What seems to be going on is that PulseAudio remembered the associations of certain programs (e.g., VLC) with the headphones, but only for those programs where I had at some point manually changed the output sink manually via pavucontrol. The solution here was two-step:

  1. In /etc/pulse/default.pa, replace the line that says:

    load-module module-stream-restore


    load-module module-stream-restore restore_device=false

    This will make PulseAudio not try to remember associations between specific programs and devices. From now on, all programs get the default sources/sinks when they connect to PulseAudio.

  2. Set the default sink manually to the Bluetooth headphones. Use pactl list sinks to figure out the sink name:

    $ pactl list sinks  | grep Name:
            Name: alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp_5__sink
            Name: alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp_4__sink
            Name: alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp_3__sink
            Name: alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp__sink
            Name: bluez_sink.C8_7B_23_9F_B3_21.a2dp_sink

    Then set it (replacing with the appropriate device name):

    pactl set-default-sink bluez_sink.C8_7B_23_9F_B3_21.a2dp_sink

The result of this is that PulseAudio will remember the headphones as the default sink device when they are present, but will revert to the built-in sound card when not.

I still don’t know why this works even though the Bluetooth device’s priority is lower than the built-in sound card. (There is a PulseAudio module called module-switch-on-connect that provides behavior like this, but it is not enabled on my system, and it does not show up as loaded in the pactl list output.) But It Works For Me™.

Comentários / Comments

The day I almost switched to Wayland

2023-10-10 19:50 +0100. Tags: comp, unix, x11, wayland, in-english

A couple of days ago, I decided to give Wayland a go. For those of you who live under a rock, Wayland is meant to be the successor of X11, the traditional graphical server/protocol in the GNU/Linux world, responsible for things such as drawing windows on your screen, passing keyboard and mouse events to the correct programs, etc. (I live under a rock too, but sometimes I stretch my head out to see what is going on in the world, only to crawl back not long after.)

Like with most technology transitions, there is a lot of drama going around Wayland vs. X11 and their respective merits and demerits. Reading such discussions can be quite frustrating; not only people can have widely different usage and requirements from graphics functionality – gaming (with different kinds of games), watching videos with varying resolutions and refresh rates, different graphics cards, accessibility settings, desktop environments, etc. –, but people can also differ in how they perceive little functionality changes just because of variation in how their eyes or brains work. A relatively minor glitch (such as screen tearing while playing a video, a couple milliseconds extra delay to process a keystroke in a game, or a font that renders slightly differently) can be a huge annoyance to one person, barely noticeable to another, and literally invisible to a third one. The result is that such discussions feel like people are talking past one another, unable to understand why others would make a different choice from them and insist on being wrong on the internet. People in the GNU/Linux world are also used to enjoying immense freedom to choose, customize and build their own desktop environments, which also contributes to the wide variety of experiences and difficulties in switching from one graphical stack to another.

If you want to understand why Wayland exists, you can watch The real story behind Wayland and X, a Linux.conf.au presentation by Daniel Stone, a Wayland and former X.org developer. Basically, X.org contains a huge number of features that are not used by modern clients but have to be kept around for compatibility, and limit the ways in which problems with X can be solved. Originally, the X server used to be responsible for font rendering, drawing graphical primitives, and a variety of other functions that nowadays are done by the clients themselves; modern clients usually just want to send a fully rendered image for the server to display. All the old cruft accumulated across the four decades of X11’s existence make it hard to maintain for developers.

One thing that is noticeable in these discussions about X11 vs Wayland is that one hears a lot from X11 users defending X11, Wayland users defending Wayland, Wayland developers defending Wayland, but not much from X11 developers. The main reason for this is that X11 developers are Wayland developers by and large. The X.org server is pretty much in maintenance mode, and much if not most of development that still goes on in the xserver repo is related to Xwayland, the compatibility layer that allows X11 clients to run on Wayland. As much as we may like X.org, if developers don’t want to work on it, there’s not much we can do about it (and it seems that it’s pretty hard for new developers to get started on it, due to the accumulated complexity). Granted, X.org isn’t going away any time soon, but it’s also not going anywhere. Regardless of the technical merits of Wayland vs. X11, it seems pretty clear that Wayland is the future going forward.

First impressions

So far I have stayed in the comfort of my old X11 setup, mainly because I had no reason to put an effort into switching. A reason finally showed up, though: on my current laptop (a ThinkPad E14 4th generation), I see quite a bit more tearing while watching videos than on my previous PCs. Although it is within the range of what I can live with (after all I’ve been using this computer for almost a year like this), all else being equal, it’s something I would like to get rid of.

The first step into switching to Wayland is picking a compositor: the application responsible for managing windows and drawing on the screen. On X11, the window manager and the X server are two different programs; on Wayland, both of these roles are taken by the compositor. The idea here is to cut out the middleman since (1) nowadays the graphics card driver lives in the kernel, which exposes it as a framebuffer device, unlike in the olden days where you would have different X drivers to handle different graphics cards, and (2) most modern window managers do compositing anyway, so instead of having the window manager composite the image of the whole desktop, then give it to X to draw it on the screen, the compositor can write it directly to the graphics card.

This means that there are effectively as many Wayland servers as there are window managers out there. This is annoying because the compositor is not only responsible for managing windows, but also handling input devices, keyboard layouts, accessibility features, clipboard, and a variety of other things that were traditionally handled by the X server. Each compositor has to implement these features on its own, and although there are common libraries that are used by different compositors to implement some of these features (e.g., libinput), there is often no standard way to access those features that is portable across different compositors. For instance:

Some of these features may end up being standardized as protocol extensions (see wlr-protocols and wayland-protocols), but which protocols will be supported by each compositor will vary. This feels like the situation in Scheme with its various SRFIs that different implementations may or may not support, or XMPP where support for a feature depends on the client and server supporting the desired set of extensions. I suppose the situation will improve in the upcoming years as the set of protocol extensions gets more standardized, but the current situation is this. The thing is that this is a non-issue in X: new window managers don’t need to care about any of this, because the X server handles these the same way regardless of what is your window manager.

As soon as I open the Sway session and start up lxterminal, I notice an issue: lxterminal on Wayland is not honoring my FREETYPE_PROPERTIES='truetype:interpreter-version=35' environment variable. This setting changes the font rendering algorithm such that fonts look crispier, especially in non-HiDPI displays. This is well within the “some people won’t even notice” category, but for me the difference is noticeable (particularly in my external display), it took me ages to figure out this setting existed, and I’m not willing to give it up easily (at least not until I switch to an HiDPI external display, something that probably won’t happen within the next couple of years). I noticed that Emacs did not suffer from this issue, but it turns out Emacs was running under Xwayland. It’s nice indeed to see that X11 apps run seamlessly enough under Wayland that it took me some work to realize that it was running under Xwayland and not natively. (I figured it out by calling xprop: it only reacts to clicks on X11 windows.) I installed foot, a lightweight native Wayland terminal that is recommended by the sway package on Debian, and it also suffers from this issue. So it seems to be a general issue with Freetype under Wayland, which is weird because font rendering should be a client-side problem and should be the same under X11 and Wayland.

Finally, I tried to start up the NetworkManager applet. Just running nm-applet won’t show anything, because by default nm-applet uses the Xembed protocol to create a tray icon, which is not supported by Swaybar; you have to run nm-applet --indicator instead. However, clicking on the icon does nothing; it seems that the context menu on tray icons is currently not supported (as of Sway 1.7). It does work with Waybar (and the context menu has the same font rendering issue; in fact I’m not even sure it’s using the same font), but Waybar has a lot more bells and whistles I’m not interested in, plus I would have to figure out how to adapt my current status bar script to it (assuming it’s possible), which is pretty important to me as I use the i3 status bar to display desktop notifications.

The lack of Xembed tray icon support is a problem for another program I use as well: Thunderbird. As of 2023, I’m still using Thunderbird 52 (released in 2018) because it’s the last version that supports FireTray, which shows me a glorious tray icon with a number of unread messages whenever there are unread messages in selected folders, and no icon otherwise. I know that one day I will probably have to switch to a different mail client and/or workflow, but that day is not going to be now.

It does eliminate screen tearing, though. But also it turns out I could fix that on X.org by using picom --backend glx --vsync. [Update: Actually that doesn't fully fix it, and sometimes causes other glitches of its own. Wayland wins in this regard.]

Crawling back to under my rock

In The technical merits of Wayland are mostly irrelevant, Chris Siebenmann argues that everyone who would switch from X to Wayland by virtue of its technical merits has already switched. The people who haven’t done so fall into a bunch of categories, one of which is:

People using desktop environments or custom X setups that don’t (currently) support Wayland. Switching to Wayland is extremely non-transparent for these people because they will have to change their desktop environment (so far, to GNOME or KDE) or reconstruct a Wayland version of it.

I happen to be in this category. Sway can mostly replace i3, but then I have to find a replacement for nm-applet (or use Waybar and change my status bar script), a replacement for FireTray, rewrite my jump-to-window script, figure out what the heck is going on with the font rendering, change my scripts that currently use xrandr, etc. All of this to get a desktop just as good as my current X11 one; switching to Wayland does not really bring me any new functionality or improvements. Maybe a few years from now, as X.org starts to bit-rot and new stuff starts to be developed for Wayland exclusively, switching will become more compelling. As of 2023, though, I don’t really have much to gain from it, and I’d rather spend my time on other adventures, at least for now.

But I don’t share the Wayland hate I see in various places around the interwebs. When the time comes (or when I feel like it), I will make the switch. By then, hopefully some of the issues above will have been fixed (e.g., more standardized protocol extensions, menu support for Swaybar tray icons), hopefully I will have found a replacement for FireTray, and maybe I will have switched to a HiDPI external monitor. Until then, long live X.org.

2 comentários / comments

Lenovo L22e-20 screen brightness and XRandR mode

2021-02-07 12:41 +0000. Tags: comp, unix, x11, in-english

Computer screens are a complicated business for me: most screens are too bright for my eyes even at the zero brightness setting. I have had some luck with the most recent laptops I used, though – a Dell Latitude 7490 I bought second-hand last year, and an HP Pavillion Gaming Laptop provided by my company, both of which have excellent screens – so I wondered if maybe monitor technology had improved enough lately that I would be able to get an external monitor that won’t burn my eyes after a few hours use. So I decided to try my luck with a Lenovo L22e-20 monitor.

When it arrived, I tried it out and was immediately disappointed. It was just as bad brightness-wise as every other LCD external monitor I had used. Even at the zero brightness setting, the black background was not black, but dark grey, which made the contrast too low. The image quality was really good for watching videos, but for staring at text for extended periods of time, it was just not comfortable to look at; my laptop screen was much better. I was so disappointed that I decided I would return the monitor and order a different one.

A couple of days later, I decided to try it again, and to my great surprise, the monitor did not look bad at all. The black was really black, the brightness was pretty good (though I wish I could lower it a little bit further at night). I wondered if I had just gotten used to the new monitor, but the difference was so great that it was hard to believe it was just a psychological effect.

A few hours later, I wanted to see how i3 would handle workspaces when screens are disconnected or connected to a running session. So I disconnected the Lenovo monitor and connected it again – and to my even greater surprise the screen came back with the awful brightness of the first time. I tried to look at every setting in the monitor, but nothing had changed; I tried disconnecting and connecting again, to no avail; I tried to turn everything off and on again – nothing changed, same awful brightness.

The next day, I decided to look at XRandR settings – maybe it was some software-side gamma or brightness setting or something that was affecting the brightness. I ran xrandr --verbose, and gamma/brightness values were normal, but I noticed something else: there were five different 1920x1080 modes for the screen. This is the relevant part of the output:

  1920x1080 (0xa4) 148.500MHz +HSync +VSync *current +preferred
        h: width  1920 start 2008 end 2052 total 2200 skew    0 clock  67.50KHz
        v: height 1080 start 1084 end 1089 total 1125           clock  60.00Hz
  1920x1080 (0xa5) 174.500MHz +HSync -VSync
        h: width  1920 start 1968 end 2000 total 2080 skew    0 clock  83.89KHz
        v: height 1080 start 1083 end 1088 total 1119           clock  74.97Hz
  1920x1080 (0xa6) 148.500MHz +HSync +VSync
        h: width  1920 start 2008 end 2052 total 2200 skew    0 clock  67.50KHz
        v: height 1080 start 1084 end 1089 total 1125           clock  60.00Hz
  1920x1080 (0xa7) 148.500MHz +HSync +VSync
        h: width  1920 start 2448 end 2492 total 2640 skew    0 clock  56.25KHz
        v: height 1080 start 1084 end 1089 total 1125           clock  50.00Hz
  1920x1080 (0xa8) 148.352MHz +HSync +VSync
        h: width  1920 start 2008 end 2052 total 2200 skew    0 clock  67.43KHz
        v: height 1080 start 1084 end 1089 total 1125           clock  59.94Hz

Here, besides the resolution, frequencies, and other characteristics, each mode is identified by a hexadecimal code in parentheses (0xa4, 0xa5, etc.). Turns out you can pass those codes to the xrandr --mode option instead of a resolution such as 1920x1080 to select one among multiple modes with the same resolution.

I decided to try the other modes, just to see what difference it would make – and lo and behold, the second mode made the screen brightness good again! All the other modes left the screen with the bright background. I don’t know what it is specifically about this mode that had an effect on brightness, but I notice two things: it is the mode with the highest frequency, and it is the only one with -VSync rather than +VSync (the xorg.conf manpage tells us this is the polarity of the VSync signal, whatever that is). Maybe one (or both) of these elements is involved in the trick.

Actually, even if you run xrandr without the --verbose option, it will list potentially multiple modes for each resolution, by showing all available refresh rates for each resolution:

HDMI-1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 476mm x 268mm
   1920x1080     60.00 +  74.97*   60.00    50.00    59.94  
   1920x1080i    60.00    50.00    59.94  
   1680x1050     59.88  
   1280x1024     75.02    70.00    60.02  
   1440x900      59.90  
   1152x864      75.00  
   1280x720      60.00    60.00    50.00    59.94  
   1024x768      75.03    70.07    60.00  
   800x600       72.19    75.00    60.32  
   720x576       50.00    50.00    50.00  
   720x480       60.00    60.00    59.94    59.94    59.94  
   640x480       75.00    72.81    60.00    59.94    59.94  
   720x400       70.08  

I had never paid much attention to this, but you can actually select the specific mode you want by calling, for example, xrandr --output HDMI-1 --mode 1920x1080 --rate 74.97, specifying both the resolution and the refresh rate. In some cases, though, there are multiple modes with the same refresh rate (for example, the 720x576 line above has three different modes with the same refresh rate 50.00); in this case, I think the only way to choose a specific mode is to specify the hexadecimal code of the mode listed by the --verbose option.

If you don’t specify a refresh rate or give a specific mode hex code, XRandR will theoretically select the “preferred” mode, which is the one with a + sign after it in the output. For this Lenovo monitor, the preferred mode is a bad one, so you have to override it with these options.

The weirdest thing about this story is that, on the day the monitor was suddenly good, Xorg had apparently selected a non-preferred mode by pure chance for some reason. If that had not happened, I would probably have never discovered that the monitor had a good mode at all.

2 comentários / comments

My terrible nginx rules for cgit

2020-08-16 12:40 +0100. Tags: comp, unix, web, in-english

I’ve been using cgit for hosting my Git repositories since about the end of 2018. One minor thing that annoys me about cgit is that the landing page for repositories is not the about page (which shows the readme), but the summary page (which shows the last commits and repository activity). This is a useful default if you are visiting the page of a project you already know (so you can see what’s new), but not so much for someone casually browsing your repos.

There were (at least) two ways I could solve this:

I went with the second option.

Things are not so simple, however: even if I map my external URLs into cgit’s internal ones, cgit will still generate links pointing to its own version of the URLs. So the evil masterplan is to have two root locations:

In the rest of this post, I will go through the nginx rules I used. You can find them here if you would like to see them all at once.

The cgit FastCGI snippet

We will need four different rules for the /code location. All of them involve passing a translated URL to cgit, which involves setting up a bunch of FastCGI variables, most of which are identical in all rules. So let’s start by creating a file in /etc/nginx/snippets/fastcgi-cgit.conf which we can reuse in all rules:

fastcgi_pass unix:/run/fcgiwrap.socket;

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# Tell fcgiwrap about the binary we’d like to execute and cgit about
# the path we’d like to access.
fastcgi_param  SCRIPT_FILENAME    /usr/lib/cgit/cgit.cgi;
fastcgi_param  DOCUMENT_ROOT      /usr/lib/cgit;

These are standard CGI parameters; the only thing specific to cgit here is the SCRIPT_FILENAME and DOCUMENT_ROOT variables. Change those according to where you have cgit installed in your system.

The /code/ rules

Now come the interesting rules. These go in the server { ... } nginx section for your website (likely in /etc/nginx/sites-enabled/site-name, if you are using a Debian derivative). Let’s begin by the rule for the landing page: we want /code/<repo-name>/ to map to cgit’s /<repo-name>/about/:

    location ~ ^/code/([^/]+)/?$ {
        include snippets/fastcgi-cgit.conf;
        fastcgi_param  SCRIPT_NAME  /cgit;
        fastcgi_param  PATH_INFO    $1/about/;

The location line matches any URL beginning with /code/, followed by one or more characters other than /, followed by an optional /. So it matches /code/foo/, but not /code/foo/bar (because foo/bar has a /, i.e., it is not “one or more characters other than /”). The foo part (i.e., the repo name) will be accessible as $1 inside the rule (because that part of the URL string is captured by the parentheses in the regex).

Inside the rule, we include the snippet we defined before, and then we set two variables: SCRIPT_NAME, which is the base URL cgit will use for its own links; and PATH_INFO, which tells cgit which page we want (i.e., the <repo-name>/about page). Note that the base URL we pass to cgit is not /code, but /cgit, so cgit will generate links to URLs like /cgit/<repo-name>/about/. This is important because later on we will define rules to redirect /cgit URLs to their corresponding /code URLs.

The second rule we want is to expose the summary page as /code/<repo-name>/summary/, which will map to cgit’s repo landing page:

    location ~ ^/code/([^/]+)/summary/$ {
        include snippets/fastcgi-cgit.conf;
        fastcgi_param  SCRIPT_NAME  /cgit;
        fastcgi_param  PATH_INFO    $1/;

Again, the principle is the same: we match /code/foo/summary/, extract the foo part, and pass a modified URL to cgit. In this case, we just pass foo/ without the summary, since cgit’s repo landing page is the summary.

The third rule is a catch-all rule for all the other URLs that don’t require translation:

    location ~ ^/code/(.*) {
        include snippets/fastcgi-cgit.conf;
        fastcgi_param  SCRIPT_NAME  /cgit;
        fastcgi_param  PATH_INFO    $1;

That is, /code/ followed by anything else not matched by the previous rules is passed as is (removing the /code/ part) to cgit.

The /cgit/ rules

Now we need to do the mapping in reverse: we want cgit’s links (e.g., /cgit/<repo-name>/about) to redirect to our external version of them (e.g., /code/<repo-name>/). These rules are straightforward: for each of the translation rules we created in the previous session, we add a corresponding redirect here.

    location ~ ^/cgit/([^/]+)/about/$ {
        return 302 /code/$1/;

    location ~ ^/cgit/([^/]+)/?$ {
        return 302 /code/$1/summary/;

    location ~ ^/cgit/(.*)$ {
        return 302 /code/$1$is_args$args;

[Update (2020-11-05): The last rule must have a $is_args$args at the end, so that query parameters are passed on in the redirect.]

The cherry on the top of the kludge

This set of rules will already work if all you want is to expose cgit’s URLs in a different form. But there is one thing missing: if we go to the cgit initial page (the repository list), all the links to repositories will be of the form /cgit/<repo-name>/, which our rules will translate to /code/<repo-name>/summary/. But we don’t want that! We want the links in the repository list to lead to the repo about page (i.e., /code/<repo-name>/, not /cgit/<repo-name>/). So what do we do now?

The solution is to pass a different base URL to cgit just for the initial page. So we add a zeroth rule (it has to come before all other /code/ rules so it matches first):

    location ~ ^/code/$ {
        include snippets/fastcgi-cgit.conf;
        fastcgi_param  SCRIPT_NAME  /code;
        fastcgi_param  PATH_INFO    /;

The difference between these and the other rules is that we pass SCRIPT_NAME with the value of /code instead of /cgit, so that in the initial page, the links are of the form /code/<repo-name>/ instead of /cgit/<repo-name>/, which means they will render cgit’s /<repo-name>/about/ page instead of /<repo-name>/.

Beautiful, huh?


One thing you have to ensure with these rules is that every repo has an about page; cgit only generates about pages for repos with a README, so your links will break if your repo doesn’t have one. One solution for this is to create a default README which cgit will use if the repo does not have a README itself. For this, I have the following settings in my /etc/cgitrc:

# Use the repo readme if available.

# Default README file. Make sure to put this file in a folder of its own,
# because all files in the folder become accessible via cgit.


That’s all I have for today, folks. If you have comments, feel free to, well, leave a comment.

4 comentários / comments

Switching to the i3 window manager

2020-07-19 16:00 +0100. Tags: comp, unix, wm, emacs, in-english

After almost 3 years using EXWM as my window manager, I decided to give i3 a try. And after two weeks using it, I have to say I'm definitely sticking with it.

I had actually tried i3 years ago, but I had never used a tiling window manager before, and for some reason it didn't click for me at the time. This time, after a long time using EXWM (which I picked up more easily at the time since all the commands were the regular Emacs window/buffer commands I already knew), i3 was quite easy to pick up. So here I am.

Why not EXWM?

EXWM has a lot going for it, mainly from the fact of running in Emacs, and therefore benefitting from the general powers that all things built on Emacs have: it's eminently hackable and customizable (and you can generally see the results of your hacks without even restarting it), and can be integrated in your Emacs workflow in various ways (I gave some examples in my previous EXWM post).

However, it also has some drawbacks. EXWM does not really do much in the way of managing windows: essentially, EXWM just turns all your windows into Emacs buffers, and the window management tasks proper (splitting, deciding which Emacs window will display a new X window, etc.) is the built-in window management Emacs uses for its own windows. Which can of course be customized to death, but is not particularly great for large numbers of windows, in my opinion.

Another problem with EXWM is that if Emacs hangs for any reason (e.g., waiting for TRAMP to open a remote SSH file, or syntax highlighting choking on an overly long line in a JSON file or a Python shell), your whole graphical session freezes, because EXWM does not get an opportunity to react to X events while Emacs is hung doing other stuff. This will happen more or less often depending on the kinds of tasks you do with Emacs. (Also, if you have to kill Emacs for any reason, you kill your entire graphical session, though this can be avoided by starting Emacs like this in your .xsession:

until emacs; do :; done

an idea I wish I had had earlier.)

Finally, EXWM is glitchy. Those glitches don't manifest too often, and it's hard to separate the glitches that come naturally with it from the ones caused by my own hacks, but the fact is that I got tired of the glitchiness and hanging, and also I was lured by i3's tabs support, so I decided to switch.

First steps into i3

The first time you start i3, it presents you with a dialog asking whether you want to use Alt or Win (the 'Windows' key, a.k.a. Super) as the modifier key for i3 shortcuts. I recommend choosing Super here, since it will avoid conflicts with shortcuts from applications. i3 will then generate a config file at ~/.config/i3/config.

The generated config file contains all the default keybindings; there are no extra keybindings other than those listed in this file. This is good because you can peruse the config file to have a general idea of what keybindings exist and how their corresponding commands are expressed. That being said, the i3 User's Guide is quite good as well, and you should at least skim over it to get an idea of i3's abilities.

One peculiar thing about the standard keybindings is the use of Super+j/k/l/; to move to the window to the left, down, up, and right, respectively. That's shifted one key to the right of the traditional h/j/k/l movement commands used by Vim and some other programs. The documentation justifies this as being easier to type without moving away from the home row (and also, Super+h is used to set horizontal window splitting), but I ended up changing this to Super+h/j/k/l simply for the convenience of having bindings similar to what other applications use (and then moving horizontal splitting to Super+b, right beside Super+v for vertical splitting).

Unlike EXWM or some other window managers, the i3 config file is not a full-fledged programming language, so it's not as flexible as those other WMs. However, i3 has a trick up its sleeve: the i3-msg program, which allows sending commands to a running i3. Thanks to i3-msg, you can do tasks that require more of a programming language (e.g., conditional execution) by writing small shell scripts. For example, I have a script called jump-to-terminal.sh, which is just:

i3-msg '[class="terminal"] move container to workspace current, focus' | grep -q true || x-terminal-emulator

i.e., try to find an open terminal window and move it to the current workspace; if the operation does not succeed (because there is no open terminal window), open a new terminal. I can then bind this script to a shortcut in the i3 config file. (I've actually changed script later to not move the window to the current workspace, but it shows how you can string multiple i3 commands together applying to the same window.)

Containers, tabs, and more

i3 uses the concept of containers to organize windows: windows themselves are containers (containing the actual X11 window), but the whole workspace is itself a container that can be split into multiple subcontainers that can be arbitrarily nested. Containers can either use a split layout (windows are tiled horizontally or vertically within the container), or a tabbed layout (i3 shows a tab bar at the top of the container, and each contained window is a tab), or a stacked layout (which is the same as the tabbed layout but the tab titles are placed in separate lines rather than side-by-side). You can switch the layout of the current container with the shortcuts Super+w (tabbed), Super+s (stacked), and Super+e (split, toggling between horizontal and vertical tiling).

(Note that what i3 calls "horizontal split container" is a split container with horizontal tiling orientation, i.e., windows are laid out side-by-side. This can be confusing if you expect "horizontal split" to mean that the splitting line will be horizontal. This is the same terminology that Emacs uses for window splitting, but the opposite of Vim.)

Containers can be arbitrarily nested, and you can have different layouts in each subcontainer. For example, you could have your workspace divided into two horizontally-tiled containers, and have a tabbed layout in one of the subcontainers. Note that because of this, it's important to know which container you have selected when you use the layout-changing commands. The colors of the borders tell you that, but it takes a while to get used to paying attention to it. i3 comes with a pre-defined binding Super+a to select the parent of the current container, but not one to select a child; I have found it useful to bind Super+z to focus child for this purpose.

Unnesting containers

The commands Super+v and Super+h (Super+b in my modified keymap) select a tiling orientation for new windows opened in the current container. (Again, the border colors tell you which mode is active.) It implicitly turns the current container into a nested container, so that new windows will become siblings of the current window. It is very easy to create nested containers by accident in this way, especially when you are just starting with i3. Those show up like i3: H[emacs] in the window title (i.e., a horizontally-tiled container containing just an emacs window), and you can even get into multiple levels of nested containers with a single window inside. In these situations, it is useful to have a command to move the current window back to its parent container. Surprisingly, i3 does not have a built-in command for that, but it is possible to concoct one from existing commands (based on this StackExchange answer):

# Move container to parent
bindsym $mod+Shift+a mark subwindow; focus parent; focus parent; mark parent; [con_mark="subwindow"] focus; move window to mark parent; [con_mark="subwindow"] focus; unmark

What this does is to use i3 marks (which are like Vim marks, allowing you to assign labels to windows) to mark the current window and its parent's parent, and then moving the window to inside its parent's parent (i.e., it becomes a sibling of its current parent).

The status bar

In EXWM, I had recently implemented a hack to display desktop notifications in the Emacs echo area. I hate desktop notifications appearing on the top of what I'm doing (especially when I'm coding), and I had most of them disabled for this reason until recently, but Slack notifications are useful to see at work. With this hack, I could finally have non-obtrusive desktop notifications. I was only going to switch to i3 if I could find a way to have similar functionality in it.

i3 does not exactly have an echo line, but it does have a desktop bar which shows your workspaces to the left, tray icons to the right, and the output of a status command in the middle. The status command can be any command you want, and the status line shows the last line of output the command has printed so far, so the command can keep updating it. i3bar actually supports two output formats: a plain-text one in which every line is displayed as-is in the status line, and a JSON-based format which allows specifying colors, separators and other features in the output.

This means that you can write a script to listen for D-Bus desktop notifications and print them as they come, together with whatever else you want in the status line (such as a clock and battery status), and blanking them after a while, or when a 'close notification' message is received. I have done just that, and it works like a charm. (It requires python3-pydbus to be installed.) The only problem with this is that the content of the status line is aligned to the right (because it is meant to be used for a clock and stuff like that), and there is no way to make it aligned to the left, so I actually pad the message to be shown with spaces to a length that happens to fit my monitor. It is sub-optimal, but it works well enough.


I'm pretty happy with the switch to i3. Although I've lost the deep integration with Emacs, it has actually been an improvement even for my Emacs usage, since i3 tabs supplement Emacs's lack of tabs better than any tabbing package I have seen for Emacs. (Having tabs for all programs, including things like Evince, is really nice.) If you are interested in tiling window managers and are willing to spend a few days getting used to it, I definitely recommend it.

2 comentários / comments

From Thunderbird to Liferea as a feed reader

2019-09-20 18:04 -0300. Tags: comp, unix, mundane, in-english

I've recently switched from Thunderbird to Liferea as my RSS feed reader. Thunderbird was randomly failing to update feeds at times*, and I thought it might be a good idea to use separate programs for e-mail and RSS for a change, so I went for Liferea. (I considered Elfeed too, but Elfeed does not support folders, only tags. In principle, tags can do everything folders can and more; the problem is that Elfeed cannot show a pane with all tags and the number of unread articles with each tag, the way Thunderbird or Liferea (or your average mail client) can do with folders.)

Liferea is pretty good, although I miss some shortcuts from Thunderbird, and sometimes shortcuts don't work (because focus is on some random widget). Here are some tips and tricks.

Importing feeds from Thunderbird to Liferea

Thunderbird can export the feed list in OPML format (right click on the feed folder, click Subscribe…, then Export). You can then import that on Liferea (Subscriptions > Import Feed List). No surprises here.

The tray icon

Liferea comes with a number of plugins (Tools > Plugins). By default, it comes with the Tray Icon (GNOME Classic) plugin enabled, which, unsurprisingly, creates a tray icon for Liferea. The problem with this for me is that whenever the window is 'minimized', Liferea hides the window entirely; you can only bring it back by clicking on the tray icon. I believe the idea is so that the window does not appear in the taskbar and the tray, but this interacts badly with EXWM, where switching workspaces or replacing Liferea with another buffer in the same Emacs 'window' counts as minimizing it, and after that it disappears from the EXWM buffer list. The solution I used is to disable the tray icon plugin.

Playing media

Liferea has a Media Player plugin to play media attachments/enclosures (such as in podcast feeds). To use it on Debian, you must have the gir1.2-gstreamer-1.0 package installed (it is a 'Recommends' dependency, not a mandatory one).

Alternatively, you can set Liferea to run an arbitrary command to open a media enclosure; the command will receive the enclosure URL as an argument. You can use VLC for that. The good thing about it is that VLC will start playing the stream immediately; you don't have to wait for it to download completely before playing it. The bad thing is that once it finishes playing the stream, the stream is gone; if you play it again, it will start downloading again. Maybe there is a way to configure this in VLC, but the solution I ended up using was to write a small script to start the download, wait a bit, and start VLC on the partially downloaded file. This way, the file will be fully downloaded and can be replayed (and moved elsewhere if you want to preserve it), but you don't have to wait for the download to finish.

# download-and-play-media.sh

# Save file in a temporary place.
file="/tmp/$(date "+%Y%m%d-%H%M%S").media"
# Start download in a terminal so we can see the progress.
x-terminal-emulator -e wget "$1" -O "$file" &
# Wait for the file to be non-empty (i.e, for the download to start).
until [[ -s "$file" ]]; do
    sleep 1
# Wait a bit for the file to fill.
sleep 2
# Play it.
vlc "$file"

Miscellaneous tips


So far I had two UI-related problems with Liferea:


Overall, I'm pretty satisfied with Liferea. There are a few problems, but so far I like it better than Thunderbird for feed reading.

Update (2020-03-23): After a few months using Liferea, I have to say that Thunderbird is better to use from the keyboard. Liferea is way too sensitive to which invisible thing has focus at a given moment. Were it not for Thunderbird not handling well hundreds of feeds, I think I would switch back.

Update (2020-07-10): I ended up switching to Elfeed.


* I suspect the problem was that Thunderbird was trying to DNS-resolve the domains for a huge number (perhaps all) of feeds at the same time, and some of the requests were being dropped by the network. I did not do a very deep investigation, though.

Comentários / Comments

Truques com SSH

2017-01-24 19:40 -0200. Tags: comp, unix, network, em-portugues

Pous, nos últimos tempos eu aprendi algumas coisinhas novas sobre o SSH. Neste post relato algumas delas.

Port forwarding

O SSH possui duas opções, -L e -R, que permitem encaminhar conexões de uma porta local para um host remoto e vice-versa.

Porta local para um host remoto

Imagine que você está na sua máquina local, chamada midgard, e há uma máquina remota, chamada asgard, que é acessível por SSH. Você quer acessar um serviço na pora 8000 da máquina asgard a partir da máquina midgard, mas você quer tunelar o acesso por SSH (seja porque você quer que o acesso seja criptografado, ou porque a porta 8000 simplesmente não é acessivel remotamente). Você pode usar o comando:

midgard$ ssh -L 9000: fulano@asgard

O resultado disso é que conexões TCP feitas para sua porta local 9000 serão tuneladas através da conexão com fulano@asgard para o endereço, porta 8000 na outra ponta. Por exemplo, se asgard tem um servidor web ouvindo na porta 8000, agora você vai poder abrir um browser em midgard, apontar para http://localhost:9000, e a conexão vai cair na porta 8000 de asgard, tudo tunelado por uma conexão SSH.

Note que o é o endereço de destino do ponto de vista do servidor. Você poderia usar outro endereço para acessar outras máquinas na rede do servidor. Por exemplo, se a máquina vanaheim é acessível a partir de asgard, você poderia rodar:

midgard$ ssh -L 9000:vanaheim:8000 fulano@asgard

e agora todos os acessos à porta TCP 9000 da sua máquina local cairão na porta 8000 de vanaheim, tunelados através da conexão SSH com asgard.

Opcionalmente, você pode especificar um "bind address" antes da porta local, para especificar que apenas a porta 9000 de uma interface de rede específica deve ficar ouvindo por conexões. Por exemplo, você pode usar:

midgard$ ssh -L localhost:9000:vanaheim:8000 fulano@asgard

para dizer que a porta deve escutar apenas conexões da própria máquina. (Por padrão, que interfaces serão usadas é decidido pela opção GatewayPorts do cliente SSH, que defaulta para ouvir apenas na interface local de qualquer forma.) Alternativamente, pode-se passar um bind address vazio (i.e., :9000:vanaheim:8000, sem nada antes do primeiro :), para ouvir em todas as interfaces. Dessa maneira, outras máquinas na sua rede local que acessem a porta 9000 de midgard também terão o acesso tunelado para a porta 8000 de asgard. (* também funciona ao invés da string vazia, mas aí você tem que escapar o * para o shell não tentar expandir.)

Porta remota para a máquina local

Também é possível fazer o contrário: instruir o servidor SSH remoto a redirecionar alguma de suas portas para uma máquina e porta acessível a partir da sua máquina local. Para isso, utiliza-se a opção -R. Por exemplo:

midgard$ ssh -R 8000:localhost:22 fulano@asgard

Isso faz com que a porta 8000 em asgard seja tunelada para a porta 22 da máquina local. Agora, se alguém na máquina asgard acessar a porta 8000 (por exemplo, com ssh -p 8000 beltrano@localhost), a conexão vai cair na sua porta 22 local (e a pessoa terá acesso ao seu servidor SSH local). Você pode usar isso se você está atrás de um firewall ou NAT e a máquina remota é acessível pela Internet, mas a sua máquina local não, e você quer dar acesso a algum serviço da sua máquina local à máquina remota. (Já abordamos isso por aqui antes, mas menciono de novo for completeness.)


O SSH é capaz de funcionar como um proxy SOCKS. Para isso, utiliza-se a opção -D ("dynamic forwarding"):

midgard$ ssh -D localhost:8000 fulano@asgard

Isso faz com que o SSH ouça como um servidor SOCKS na porta 8000 da máquina local. Conexões recebidas nessa porta serão tuneladas para a máquina asgard, que funcionará como um proxy. Você pode então apontar o proxy SOCKS do seu browser ou outra aplicação para localhost, porta 8000.

Outras opções úteis

-C habilita compressão da conexão. E útil principalmente com conexões lentas (numa rede local, a compressão não compensa muito).

Por padrão, se você usa um dos comandos de redirecionamento de portas acima, o SSH faz o redirecionamento e abre uma sessão de shell comum. Se você quer apenas fazer o redirecionamento, pode usar as opções -N (não executa comando remoto) e -f (vai para background (forks) depois de pedir a senha). As opções podem ser combinadas em um único argumento (e.g., -CNf).

Escapes e comandos especiais

Em uma sessão SSH, a seqüência ENTER ~ é reconhecida como um prefixo de escape para acessar uma série de comandos especiais. Se você digitar ENTER ~ ?, verá uma lista de todos os comandos disponíveis:

Supported escape sequences:
 ~.   - terminate connection (and any multiplexed sessions)
 ~B   - send a BREAK to the remote system
 ~C   - open a command line
 ~R   - request rekey
 ~V/v - decrease/increase verbosity (LogLevel)
 ~^Z  - suspend ssh
 ~#   - list forwarded connections
 ~&   - background ssh (when waiting for connections to terminate)
 ~?   - this message
 ~~   - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

O comando ENTER ~ C abre um prompt onde é possível fazer e cancelar redirecionamentos de porta, com uma sintaxe análoga à das opções vistas anteriormente:

ssh> ?
      -L[bind_address:]port:host:hostport    Request local forward
      -R[bind_address:]port:host:hostport    Request remote forward
      -D[bind_address:]port                  Request dynamic forward
      -KL[bind_address:]port                 Cancel local forward
      -KR[bind_address:]port                 Cancel remote forward
      -KD[bind_address:]port                 Cancel dynamic forward



O uso das opções de redirecionamento pode ser controlado/desabilitado na configuração do servidor. Consulte a man page sshd_config(5) para mais informações.

2 comentários / comments

Making new Pidgin conversation windows not appear on top of other windows in Xfce

2016-02-14 17:05 -0200. Tags: comp, unix, wm, in-english

Recently I started using Xfce/Xfwm instead of IceWM. One of the problems I had after migrating is that Xfwm places new Pidgin conversation windows on the top of existing windows, obstructing whatever I am doing. At least it doesn't give focus to the new window, but ideally the window should be placed in background.

The solution I found was, well, modifying the source code. Fortunately, there is only a single line to be changed.

  1. Download Xfwm 4.12's source code, unpack it (tar -xvf xfwm4-4.12.0.tar.bz2) in a place of your choosing, and enter the xfwm4-4.12.0 directory.

  2. In the file src/focus.c, replace line 232, which says:

                clientRaise (c, None);


                clientLower (c, None);
  3. Run ./configure. If it complains about missing libraries, you will have to install the corresponding development (header) packages from your distribution. On Debian, Ubuntu and similar, these packages have names ending in -dev. For instance, if ./configure complains that libxfce4ui-1 is missing, you will have to install the package libxfce4ui-1-dev.

    Update: You can install all(?) dependencies with:

    sudo apt-get install intltool libx11-dev libgtk2.0-dev libxfce4util-dev libxfce4ui-1-dev libwnck-dev 
  4. After ./configure succeeds, run make, then make install.

  5. If everything succeeded, test the newly compiled window manager by running xfwm4 --replace. (You may have to specify the full path, e.g., /usr/local/bin/xfwm4 --replace, to make sure you are using the newly compiled Xfwm, not the one that came with your distro.) If the new window manager works, you are done! (If anything goes wrong, you can run make uninstall to undo the installation, then run /usr/bin/xfwm4 --replace to re-run the original Xfwm.)

One day, maybe, I may try to turn this into a configuration option in Xfwm and submit a patch. One day...

3 comentários / comments

Trocando de janela pelo nome

2015-06-23 02:50 -0300. Tags: comp, unix, em-portugues

Por uma série de acidentes enquanto experimentava window managers hoje,

  1. Eu me dei conta de que seria bem conveniente poder trocar de janela digitando uma parte do título ao invés de procurar na barra de tarefas ou dar vinte Alt+Tabs até encontrar a janela. (Eu me dei conta disso quando, depois de não conseguir achar a janela que eu queria no i3, a minha primeira reação foi querer dar C-x b (o comando switch-buffer do Emacs) e digitar o nome da janela.)
  2. Eu descobri ferramentas apropriadas para tornar isso possível.

[Screenshot do programa 'dmenu']

Você vai precisar de:

Com isso, podemos escrever um pequeno script para apresentar um menu e selecionar a janela escolhida.

O script


    wmctrl -xl |
        #         \1       \2       \3       \4       \5
        #         win-id   desktop  class    hostname title
        sed -r 's|([^ ]* +)([^ ]* +)([^ ]* +)([^ ]* +)(.*)|\1 \3 \5|' |
        sort -k 3 |
        dmenu -i -l 10
    )" || exit 1

wmctrl -ia "${option%% *}"

wmctrl -l lista as janelas existentes. A opção -x inclui a classe da janela na listagem. O sed não é estritamente necessário, mas deixa a lista menos poluída removendo campos desnecessários; você pode alterar essa linha para escolher os campos. O ID da janela é meio irrelevante para o usuário, mas precisamos dele para poder passá-lo ao wmctrl para ativar a janela.

sort -k 3 ordena o menu pelo título da janela. Você pode comentar essa linha fora se não quiser ordenar a lista, ou mudar os parâmetros para obter uma ordem diferente (e.g., sort -k 2 para ordenar pela classe).

Quanto ao dmenu, a opção -i faz com que ele ignore maiúsculas vs. minúsculas ao filtrar as opções pelo texto digitado pelo usuário. -l 10 indica que queremos uma opção por linha, e que no máximo 10 linhas devem ser mostradas de cada vez. Por padrão, o dmenu usa apenas uma linha e mostra as opções lado a lado. (Uma coisa meio ruim do dmenu é que ele não dá nenhuma indicação de que é possível scrollar o menu; ele só mostra as primeiras N opções e as demais ficam escondidas.)

wmctrl -a JANELA ativa a primeira janela cujo título contenha a string specificada. Como queremos que a seleção seja inambígua, utilizamos a opção -i, que permite especificar o ID da janela ao invés do título. Para extrair o ID da seleção, removemos tudo depois do primeiro espaço na string ("${option%% *}").


Salve o script no local de sua preferência, dê permissão de execução a ele (chmod +x nome-do-script), e associe-o a alguma tecla de atalho no seu ambiente gráfico favorito. Por exemplo, no IceWM isso pode ser feito adicionando no ~/.icewm/keys uma linha como:

key "Super+Tab" /caminho/do/script

substituindo Super+Tab pelo atalho de sua preferência (Super é a tecla "janelinha").

Para mais informações e possibilidades, consulte a manpage dos programas.

2 comentários / comments

Main menu

Recent posts

Recent comments


em-portugues (213) comp (152) prog (74) in-english (66) life (49) pldesign (40) unix (39) lang (32) random (28) about (28) mind (26) lisp (25) fenius (22) mundane (22) web (20) ramble (18) img (13) hel (12) rant (12) privacy (10) scheme (10) freedom (8) copyright (7) bash (7) esperanto (7) academia (7) lash (7) music (7) shell (6) mestrado (6) home (6) misc (5) emacs (5) android (5) conlang (5) worldly (4) php (4) book (4) editor (4) latex (4) etymology (4) politics (4) c (3) tour-de-scheme (3) network (3) film (3) kbd (3) ruby (3) wrong (3) security (3) llvm (2) poem (2) wm (2) cook (2) philosophy (2) treta (2) audio (2) comic (2) x11 (2) lows (2) physics (2) german (1) ai (1) perl (1) golang (1) translation (1) wayland (1) en-esperanto (1) old-chinese (1) kindle (1) pointless (1)


Quod vide

Copyright © 2010-2024 Vítor De Araújo
O conteúdo deste blog, a menos que de outra forma especificado, pode ser utilizado segundo os termos da licença Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.

Powered by Blognir.