Decomposing a function into its even and odd parts
As I have mentioned before, I am not a sudden-flash-of-insight
person. Every once in a while it happens, but usually my thinking style is to
minutely examine a large mass of examples and then gradually
synthesize some conclusion about them. I am a penetrating but slow
thinker. But there have been a few occasions in my life when the
solution to a problem struck me suddenly out of the blue.
One such occasion was on the first day of my sophomore honors physics
class in 1987. This was one of the best classes I took in my college
career. It was given by Professor Stephen Nettel, and it was about
resonance phenomena. I love when a course has a single overarching
theme and proceeds to examine it in detail; that is all too rare. I
deeply regret leaving my copy of the course notes in a restaurant in
1995.
The course was very difficult, But also very satisfying. It was also
somewhat hair-raising, because of Professor Nettel's habit of saying,
all through the second half “Don't worry if it doesn't seem to make
any sense, it will all come together for you during the final exam.”
This was not reassuring. But he was right! It did all come
together during the final exam.
The exam had two sets of problems. The problems on the left side of
the exam paper concerned some mechanical system, I think a rod fixed
at one end and free at the other, or something like that. This set of
problems asked us to calculate the resonant frequency of the rod, its
rate of damping at various driving frequencies, and related matters.
The right-hand problems were about an electrical system involving a
resistor, capacitor, and inductor. The questions were the same, and
the answers were formally identical, differing only in the details: on
the left, the answers involved length, mass and stiffness of the rod,
and on the right, the resistance, capacitance, and inductance of the
electrical components. It was a brilliant exam, and I have never
learned so much about a subject during the final exam.
Anyway, I digress. After the first class, we were assigned homework.
One of the problems was
Show that every function is the sum of an even function and an odd
function.
(Maybe I should explain that an even function is one which is
symmetric across the !!y!!-axis; formally it is a function !!f!! for
which !!f(x) = f(-x)!! for every !!x!!. For example, the function
!!x^2-4!!, shown below left. An odd function is one which is
symmetric under a half-turn about the origin; formally it satisfies
!!f(x) = -f(-x)!! for all !!x!!. For example !!\frac{x^3}{20}!!, shown
below right.)
I found this claim very surprising, and we had no idea how to solve
it. Well, not quite no idea: I knew that functions could be expanded in
Fourier series, as the sum of a sine
series and a cosine series, and the sine part was odd while the cosine
part was even. But this seemed like a bigger hammer than was
required, particularly since new sophomores were not expected to know
about Fourier series.
I had the privilege to be in that class with
Ron Buckmire, and I
remember we stood outside the class building in the autumn sunshine
and discussed the problem. I might have been thinking that perhaps
there was some way to replace the negative part of !!f!! with a
reflected copy of the positive part to make an even function, and
maybe that !!f(x) + f(-x)!! was always even, when I was hit from the
blue with the solution:
$$
\begin{align}
f_e(x) & = \frac{f(x) + f(-x)}2 \text{ is even},\\
f_o(x) & = \frac{f(x) - f(-x)}2 \text{ is odd, and}\\
f(x) &= f_e(x) + f_o(x)
\end{align}
$$
So that was that problem solved. I don't remember the other three
problems in that day's homework, but I have remembered that one ever
since.
But for some reason, it didn't occur to me until today to think about
what those functions actually looked like. Of course, if !!f!!
itself is even, then !!f_e = f!! and !!f_o = 0!!, and similarly if
!!f!! is odd. But most functions are neither even nor odd.
For example, consider the function !!2^x!!, which is neither even nor
odd. Then we get
$$
\begin{align}
f_e(x) & = \frac{2^x + 2^{-x}}2\\
f_o(x) & = \frac{2^x - 2^{-x}}2
\end{align}
$$
The graph is below left. The solid red line is !!2^x!!, and the blue
and purple dotted lines are !!f_e!! and !!f_o!!. The red line is
the sum of the blue and purple lines. I thought this was very
interesting-looking, but a little later I realized that I had already known
what these graphs would look like, because !!2^x!! is just like
!!e^x!!, and for !!e^x!! the even and odd components are exactly the
familiar !!\cosh!! and !!\sinh!! functions. (Below left, !!2^x!!; below right,
!!e^x!!.)
I wasn't expecting polynomials to be more interesting, but they were.
(Polynomials whose terms are all odd powers of !!x!!, such as !!x^{13} -
4x^5 + x!!, are always odd functions,
and similarly polynomials whose terms are all even powers of !!x!! are
even functions.) For example, consider !!(x-1)^2!!, which is neither
even nor odd. We don't even need the !!f_e!! and !!f_o!! formulas
to separate this into even and odd parts: just expand !!(x-1)^2!! as
!!x^2 - 2x + 1!! and separate it into odd and even powers, !!-2x!! and
!!x^2 + 1!!:
Or we could do !!\frac{(x-1)^3}3!! similarly, expanding it as !!\frac{x^3}3 - x^2 +
x -\frac13!! and separating this into !!-x^2 -\frac13!! and !!\frac{x^3}3 + x!!:
I love looking at these and seeing how the even blue line and the odd
purple line conspire together to make whatever red line I want.
I kept wanting to try familiar simple functions, like !!\frac1x!!, but
many of these are either even or odd, and so are uninteresting for
this application. But you can make an even or an odd function into a
neither-even-nor-odd function just by translating it horizontally,
which you do by replacing !!x!! with !!x-c!!. So the next function I
tried was !!\frac1{x+1}!!, which is the translation of !!\frac
1x!!. Here I got a surprise. I knew that !!\frac1{x+1}!! was
undefined at !!x=-1!!, so I graphed it only for !!x>-1!!. But the
even component is !!\frac12\left(\frac1{1+x}+\frac1{1-x}\right)!!,
which is undefined at both !!x=-1!! and at !!x=+1!!. Similarly the odd
component is undefined at two points. So the !!f = f_o + f_e!!
formula does not work quite correctly, failing to produce the correct
value at !!x=1!!, even though !!f!! is defined there. In general, if
!!f!! is undefined at some !!x=c!!, then the decomposition into even
and odd components fails at !!x=-c!! as well. The limit $$\lim_{x\to
-c} f(x) = \lim_{x\to -c} \left(f_o(x) + f_e(x)\right)$$ does hold, however. The
graph below shows the decomposition of !!\frac1{x+1}!!.
Vertical translations
are uninteresting: they leave !!f_o!! unchanged and
translate !!f_e!! by the same amount, as you can verify algebraically
or just by thinking about it.
Following the same strategy I tried a cosine wave. The evenness of
the cosine function is one of its principal properties, so I
translated it and used !!\cos (x+1)!!. The graph below is actually
for !!5\cos(x+1)!! to prevent the details from being too compressed:
This reminded me of the time I was fourteen and graphed !!\sin x +
\cos x!! and was surprised to see that it was another perfect
sinusoid. But I realized that there was a simple way to understand
this. I already knew that !!\cos(x + y) = \sin x\cos y + \sin y \cos
x!!. If you take !!y=\frac\pi4!! and multiply the whole thing by
!!\sqrt 2!!, you get $$\sqrt2\cos\left(x + \frac\pi4\right) =
\sqrt2\sin x\cos\frac\pi4 + \sqrt2\cos x\sin\frac\pi4 = \sin x + \cos
x$$ so that !!\sin x + \cos x!! is just a shifted, scaled cosine
curve. The decomposition of !!\cos(x+1)!! is even simpler because you
can work forward instead of backward and find that !!\cos(x+1) = \sin
x\cos 1 + \cos x \sin 1!!, and the first term is odd while the second
term is even, so that !!\cos(x+1)!! decomposes as a sum of an even and
an odd sinusoid as you see in the graph above.
Finally, I tried a
Poisson distribution, which is
highly asymmetric. The formula for the Poisson distribution is
!!\frac{\lambda^xe^\lambda}{x!}!!, for some constant !!\lambda!!. The
!!x! !! in the denominator is only defined for non-negative integer
!!x!!, but you can extend it to fractional and negative !!x!! in the
usual way by using !!\Gamma(x+1)!! instead, where !!\Gamma!! is the
Gamma function. The !!\Gamma!!
function is undefined at zero and negative integers, but fortunately
what we need here is the
reciprocal gamma function
!!\frac1{\Gamma(x)}!!, which is perfectly well-behaved. The results
are spectacular. The graph below has !!\lambda = 0.8!!.
The part of this with !!x\ge 0!! is the most interesting to me,
because the Poisson distribution has a very distinctive shape, and
once again I like seeing the blue and purple !!\Gamma!! functions
working together to make it. I think it's just great how the red line
goes gently to zero as !!x!! increases, even though the even and the
odd components are going wild. (!!x! !! increases rapidly with !!x!!,
so the reciprocal !!\Gamma!! function goes rapidly to zero. But the
even and odd components also have a !!\frac1{\Gamma(-x)}!! part, and
this is what dominates the blue and purple lines when !!x >4!!.)
On the !!x\lt 0!! side it has no meaning for me, and it's just wiggly
lines. It hadn't occurred to me before that you could extend the
Poisson distribution function to negative !!x!!, and I still can't
imagine what it could mean, but I suppose why not. Probably some
statistician could explain to me what the Poisson distribution is
about when !!x<0!!.
You can also consider the function !!\sqrt x!!, which breaks down
completely, because either !!\sqrt x!! or !!\sqrt{-x}!! is undefined
except when !!x=0!!. So the claim that every function is the sum of
an even and an odd function fails here too. Except perhaps not! You
could probably consider the extension of the square root function to
the complex plane, and take one of its branches, and I suppose it
works out just fine. The geometric interpretation of evenness and
oddness are very different, of course, and you can't really draw the
graphs unless you have four-dimensional vision.
I have no particular point to make, except maybe that math is fun,
even elementary math (or perhaps especially elementary math) and it's
fun to see how it works out.
The beautiful graphs in this article were made with
Desmos. I had dreaded having to illustrate
my article with graphs from Gnuplot (ugh) or Wolfram|α (double
ugh) and was thrilled to find such a handsome alternative.
[ Addendum: I've just discovered that in Desmos you can include a parameter in the functions that it graphs, and attach the parameter to a slider. So for example you can arrange to have it display !!(x+k)^3!! or !!e^{-(x+k)^2}!!, with the value of !!k!! controlled by the slider, and have the graph move left and right on the plane as you adjust the slider, with its even and odd parts changing in real time to match. ]
[ For example, check out travelling Gaussians or varying sinusoid. ]
[Other articles in category /math]
permanent link
Controlling the KDE screen locking works now
Yesterday
I wrote about how I was trying to control the KDE screenlocker's timeout from a shell script
and all the fun stuff I learned along the way. Then after I published
the article I discovered that my solution didn't work. But today I
fixed it and it does work.
What didn't work
I had written this script:
timeout=${1:-3600}
perl -i -lpe 's/^Enabled=.*/Enabled=False/' $HOME/.kde/share/config/kscreensaverrc
qdbus org.freedesktop.ScreenSaver /MainApplication reparseConfiguration
sleep $timeout
perl -i -lpe 's/^Enabled=.*/Enabled=True/' $HOME/.kde/share/config/kscreensaverrc
qdbus org.freedesktop.ScreenSaver /MainApplication reparseConfiguration
The strategy was: use perl to rewrite the screen locker's
configuration file, and then use qdbus to send a D-Bus message to
the screen locker to order it to load the updated configuration.
This didn't work. The System Settings app would see the changed
configuration, and report what I expected, but the screen saver itself
was still behaving according to the old configuration. Maybe the
qdbus command was wrong or maybe the whole theory was bad.
More strace
For want of anything else to do (when all you have is a hammer…), I
went back to using strace to see what else I could dig up, and tried
strace -ff -o /tmp/ss/s /usr/bin/systemsettings
which tells strace to write separate files for each process or
thread.
I had a fantasy that by splitting the trace for each process into a
separate file, I might solve the mysterious problem of the missing
string data. This didn't come true, unfortunately.
I then ran tail -f on each of the output files, and used
systemsettings to update the screen locker configuration, looking to
see which the of the trace files changed. I didn't get too much out
of this. A great deal of the trace was concerned with X protocol
traffic between the application and the display server. But I did
notice this portion, which I found extremely suggestive, even with the
filenames missing:
3106 open(0x2bb57a8, O_RDWR|O_CREAT|O_CLOEXEC, 0666) = 18
3106 fcntl(18, F_SETFD, FD_CLOEXEC) = 0
3106 chmod(0x2bb57a8, 0600) = 0
3106 fstat(18, {...}) = 0
3106 write(18, 0x2bb5838, 178) = 178
3106 fstat(18, {...}) = 0
3106 close(18) = 0
3106 rename(0x2bb5578, 0x2bb4e48) = 0
3106 unlink(0x2b82848) = 0
You may recall that my theory was that when I click the “Apply” button
in System Settings, it writes out a new version of
$HOME/.kde/share/config/kscreensaverrc and then orders the screen
locker to reload the configuration. Even with no filenames, this part
of the trace looked to me like the replacement of the configuration
file: a new file is created, then written, then closed, and then the
rename replaces the old file with the new one. If I had been
thinking about it a little harder, I might have thought to check if
the return value of the write call, 178 bytes, matched the length of
the file. (It does.) The unlink at the end is deleting the
semaphore file that System Settings created to prevent a second
process from trying to update the same file at the same time.
Supposing that this was the trace of the configuration update, the
next section should be the secret sauce that tells the screen locker
to look at the new configuration file. It looked like this:
3106 sendmsg(5, 0x7ffcf37e53b0, MSG_NOSIGNAL) = 168
3106 poll([?] 0x7ffcf37e5490, 1, 25000) = 1
3106 recvmsg(5, 0x7ffcf37e5390, MSG_CMSG_CLOEXEC) = 90
3106 recvmsg(5, 0x7ffcf37e5390, MSG_CMSG_CLOEXEC) = -1 EAGAIN (Resource temporarily unavailable)
3106 sendmsg(5, 0x7ffcf37e5770, MSG_NOSIGNAL) = 278
3106 sendmsg(5, 0x7ffcf37e5740, MSG_NOSIGNAL) = 128
There is very little to go on here, but none of it is inconsistent
with the theory that this is the secret sauce, or even with the more
advanced theory that it is the secret suace and that the secret sauce
is a D-Bus request. But without seeing the contents of the messages,
I seemed to be at a dead end.
Thrashing
Browsing random pages about the KDE screen locker, I learned that the
lock screen configuration component could be run separately from the
rest of System Settings. You use
kcmshell4 --list
to get a list of available components, and then
kcmshell4 screensaver
to run the screensaver component. I started running strace on this
command instead of on the entire System Settings app, with the idea
that if nothing else, the trace would be smaller and perhaps simpler,
and for some reason the missing strings appeared. That suggestive
block of code above turned out to be updating the configuration file, just
as I had suspected:
open("/home/mjd/.kde/share/config/kscreensaverrcQ13893.new", O_RDWR|O_CREAT|O_CLOEXEC, 0666) = 19
fcntl(19, F_SETFD, FD_CLOEXEC) = 0
chmod("/home/mjd/.kde/share/config/kscreensaverrcQ13893.new", 0600) = 0
fstat(19, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
write(19, "[ScreenSaver]\nActionBottomLeft=0\nActionBottomRight=0\nActionTopLeft=0\nActionTopRight=2\nEnabled=true\nLegacySaverEnabled=false\nPlasmaEnabled=false\nSaver=krandom.desktop\nTimeout=60\n", 177) = 177
fstat(19, {st_mode=S_IFREG|0600, st_size=177, ...}) = 0
close(19) = 0
rename("/home/mjd/.kde/share/config/kscreensaverrcQ13893.new", "/home/mjd/.kde/share/config/kscreensaverrc") = 0
unlink("/home/mjd/.kde/share/config/kscreensaverrc.lock") = 0
And the following secret sauce was revealed as:
sendmsg(7, {msg_name(0)=NULL, msg_iov(2)=[{"l\1\0\1\30\0\0\0\v\0\0\0\177\0\0\0\1\1o\0\25\0\0\0/org/freedesktop/DBus\0\0\0\6\1s\0\24\0\0\0org.freedesktop.DBus\0\0\0\0\2\1s\0\24\0\0\0org.freedesktop.DBus\0\0\0\0\3\1s\0\f\0\0\0GetNameOwner\0\0\0\0\10\1g\0\1s\0\0", 144}, {"\23\0\0\0org.kde.screensaver\0", 24}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 168
sendmsg(7, {msg_name(0)=NULL, msg_iov(2)=[{"l\1\1\1\206\0\0\0\f\0\0\0\177\0\0\0\1\1o\0\25\0\0\0/org/freedesktop/DBus\0\0\0\6\1s\0\24\0\0\0org.freedesktop.DBus\0\0\0\0\2\1s\0\24\0\0\0org.freedesktop.DBus\0\0\0\0\3\1s\0\10\0\0\0AddMatch\0\0\0\0\0\0\0\0\10\1g\0\1s\0\0", 144}, {"\201\0\0\0type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.kde.screensaver'\0", 134}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 278
sendmsg(7, {msg_name(0)=NULL, msg_iov(2)=[{"l\1\0\1\0\0\0\0\r\0\0\0j\0\0\0\1\1o\0\f\0\0\0/ScreenSaver\0\0\0\0\6\1s\0\23\0\0\0org.kde.screensaver\0\0\0\0\0\2\1s\0\23\0\0\0org.kde.screensaver\0\0\0\0\0\3\1s\0\t\0\0\0configure\0\0\0\0\0\0\0", 128}, {"", 0}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 128
sendmsg(7, {msg_name(0)=NULL,
msg_iov(2)=[{"l\1\1\1\206\0\0\0\16\0\0\0\177\0\0\0\1\1o\0\25\0\0\0/org/freedesktop/DBus\0\0\0\6\1s\0\24\0\0\0org.freedesktop.DBus\0\0\0\0\2\1s\0\24\0\0\0org.freedesktop.DBus\0\0\0\0\3\1s\0\v\0\0\0RemoveMatch\0\0\0\0\0\10\1g\0\1s\0\0",
144},
{"\201\0\0\0type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.kde.screensaver'\0",
134}]
(I had to tell give strace the -s 256 flag to tell it not to
truncate the string data to 32 characters.)
Binary gibberish
A lot of this is illegible, but it is clear, from the frequent
mentions of DBus , and from the names of D-Bus objects and methods,
that this is is D-Bus requests, as theorized. Much of it is binary
gibberish that we can only read if we understand the D-Bus line
protocol, but the object and method names are visible. For example,
consider this long string:
interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.kde.screensaver'
With qdbus I could confirm that there was a service named
org.freedesktop.DBus with an object named / that supported a
NameOwnerChanged method which expected three QString arguments.
Presumably the first of these was org.kde.screensaver and the others
are hiding in other the 134 characters that strace didn't expand.
So I may not understand the whole thing, but I could see that I was on
the right track.
That third line was the key:
sendmsg(7, {msg_name(0)=NULL,
msg_iov(2)=[{"… /ScreenSaver … org.kde.screensaver … org.kde.screensaver … configure …", 128}, {"", 0}],
msg_controllen=0,
msg_flags=0},
MSG_NOSIGNAL) = 128
Huh, it seems to be asking the screensaver to configure itself. Just
like I thought it should. But there was no configure method, so what
does that configure refer to, and how can I do the same thing?
But org.kde.screensaver was not quite the same path I had been using
to talk to the screen locker—I had been using
org.freedesktop.ScreenSaver , so I had qdbus list the methods at
this new path, and there was a configure method.
When I tested
qdbus org.kde.screensaver /ScreenSaver configure
I found that this made the screen locker take note of the updated
configuration. So, problem solved!
(As far as I can tell, org.kde.screensaver and
org.freedesktop.ScreenSaver are completely identical. They each
have a configure method, but I had overlooked it—several times in a
row—earlier when I had gone over the method catalog for
org.freedesktop.ScreenSaver .)
The working script is almost identical to what I had yesterday:
timeout=${1:-3600}
perl -i -lpe 's/^Enabled=.*/Enabled=False/' $HOME/.kde/share/config/kscreensaverrc
qdbus org.freedesktop.ScreenSaver /ScreenSaver configure
sleep $timeout
perl -i -lpe 's/^Enabled=.*/Enabled=True/' $HOME/.kde/share/config/kscreensaverrc
qdbus org.freedesktop.ScreenSaver /ScreenSaver configure
That's not a bad way to fail, as failures go: I had a correct idea
about what was going on, my plan about how to solve my problem would
have worked, but I was tripped up by a trivium; I was calling
MainApplication.reparseConfiguration when I should have been calling
ScreenSaver.configure .
What if I hadn't been able to get strace to disgorge the internals
of the D-Bus messages? I think I would have gotten the answer anyway.
One way to have gotten there would have been to notice the configure
method documented in the method catalog printed out by qdbus . I
certainly looked at these catalogs enough times, and they are not very
large. I don't know why I never noticed it on my own. But I might
also have had the idea of spying on the network traffic through the
D-Bus socket, which is under /tmp somewhere.
I was also starting to tinker with dbus-send , which is like qdbus
but more powerful, and can post signals, which I think qdbus can't
do, and with gdbus , another D-Bus introspector. I would have kept
getting more familiar with these tools and this would have led
somewhere useful.
Or had I taken just a little longer to solve this, I would have
followed up on Sumana Harihareswara’s suggestion to look at
Bustle, which is
a utility that logs and traces D-Bus requests. It would certainly
have solved my problem, because it makes perfectly clear that clicking
that apply button invoked the configure method:
I still wish I knew why strace hadn't been able to print out those
strings through.
[Other articles in category /Unix]
permanent link
Controlling KDE screen locking from a shell script
Lately I've started watching stuff on Netflix. Every time I do this,
the screen locker kicks in sixty seconds in, and I have to unlock it,
pause the video, and adjust the system settings to turn off the
automatic screen locker. I can live with this.
But when the show is over, I often forget to re-enable the automatic
screen locker, and that I can't live with. So I wanted to write a
shell script:
#!/bin/sh
auto-screen-locker disable
sleep 3600
auto-screen-locker enable
Then I'll run the script in the background before I start watching, or
at least after the first time I unlock the screen, and if I forget to
re-enable the automatic locker, the script will do it for me.
The question is: how to write auto-screen-locker ?
strace
My first idea was: maybe there is actually an auto-screen-locker
command, or a system-settings command, or something like that, which
was being run by the System Settings app when I adjusted the screen
locker from System Settings, and all I needed to do was to find out
what that command was and to run it myself.
So I tried running System Settings under strace -f and then looking
at the trace to see if it was exec ing anything suggestive.
It wasn't, and the trace was 93,000 lines long and frighting. Halfway
through, it stopped recording filenames and started recording their
string addresses instead, which meant I could see a lot of calls to
execve but not what was being execed. I got sidetracked trying to
understand why this had happened, and I never did figure it
out—something to do with a call to clone , which is like fork , but
different in a way I might understand once I read the man page.
The first thing the cloned process did was to call set_robust_list ,
which I had never heard of, and when I looked for its man page I found
to my surprise that there was one. It begins:
NAME
get_robust_list, set_robust_list - get/set list of robust futexes
And then I felt like an ass because, of course, everyone knows all
about the robust futex list, duh, how silly of me to have forgotten ha
ha just kidding WTF is a futex? Are the robust kind better than
regular wimpy futexes?
It turns out that Ingo Molnár wrote a lovely explanation of robust
futexes
which are actually very interesting. In all seriousness, do check it
out.
I seem to have digressed. This whole section can be summarized in
one sentence:
strace was no help and took me a long way down a wacky rabbit hole.
Sorry, Julia!
Stack Exchange
The next thing I tried was Google search for kde screen locker . The
second or third link I followed was to this StackExchange question,
“What is the screen locking mechanism under
KDE?
It wasn't exactly what I was looking for but it was suggestive and
pointed me in the right direction. The crucial point in the answer
was a mention of
qdbus org.freedesktop.ScreenSaver /ScreenSaver Lock
When I saw this, it was like a new section of my brain coming on line.
So many things that had been obscure suddenly became clear. Things I
had wondered for years. Things like “What are these horrible
Object::connect: No such signal org::freedesktop::UPower::DeviceAdded(QDBusObjectPath)
messages that KDE apps are always spewing into my terminal?” But now
the light was on.
KDE is built atop a toolkit called Qt, and Qt provides an interprocess
communication mechanism called “D-Bus”. The qdbus command, which I
had not seen before, is apparently for sending queries and commands on
the D-Bus. The arguments identify the recipient and the message you
are sending. If you know the secret name of the correct demon, and
you send it the correct secret command, it will do your bidding. (
The mystery message above probably has something to do with the app
using an invalid secret name as a D-Bus address.)
Often these sorts of address hierarchies work well in theory and then
fail utterly because there is no way to learn the secret names. The X
Window System has always had a feature called “resources” by which
almost every aspect of every application can be individually
customized. If you are running xweasel and want just the frame of
just the error panel of just the output window to be teal blue, you
can do that… if you can find out the secret names of the xweasel
program, its output window, its error panel, and its frame. Then you
combine these into a secret X resource name, incant a certain command
to load the new resource setting into the X server, and the next time
you run xweasel the one frame, and only the one frame, will be blue.
In theory these secret names are documented somewhere, maybe. In
practice, they are not documented anywhere. you can only extract them
from the source, and not only from the source of xweasel itself but
from the source of the entire widget toolkit that xweasel is linked
with. Good luck, sucker.
D-Bus has a directory
However! The authors of Qt did not forget to include a directory
mechanism in D-Bus. If you run
qdbus
you get a list of all the addressable services, which you can grep for
suggestive items, including org.freedesktop.ScreenSaver . Then if
you run
qdbus org.freedesktop.ScreenSaver
you get a list of all the objects provided by the
org.freedesktop.ScreenSaver service; there are only seven. So you
pick a likely-seeming one, say /ScreenSaver , and run
qdbus org.freedesktop.ScreenSaver /ScreenSaver
and get a list of all the methods that can be called on this object,
and their argument types and return value types. And you see for
example
method void org.freedesktop.ScreenSaver.Lock()
and say “I wonder if that will lock the screen when I invoke it?” And
then you try it:
qdbus org.freedesktop.ScreenSaver /ScreenSaver Lock
and it does.
That was the most important thing I learned today, that I can go
wandering around in the qdbus hierarchy looking for treasure. I
don't yet know exactly what I'll find, but I bet there's a lot of good stuff.
When I was first learning Unix I used to wander around in the
filesystem looking at all the files, and I learned a lot that way
also.
“Hey, look at all the stuff in /etc ! Huh, I wonder what's in
/etc/passwd ?”
“Hey, /etc/protocols has a catalog of protocol numbers. I wonder
what that's for?”
“Hey, there are a bunch of files in /usr/spool/mail named after
users and the one with my name has my mail in it!”
“Hey, the manuals are all under /usr/man . I could grep them!”
Later I learned (by browsing in /usr/man/man7 ) that there was a
hier(7) man page that listed points of interest, including some I
had overlooked.
The right secret names
Everything after this point was pure fun of the “what happens if I
turn this knob” variety. I tinkered around with the /ScreenSaver
methods a bit (there are twenty) but none of them seemed to be quite
what I wanted. There is a
method uint Inhibit(QString application_name, QString reason_for_inhibit)
method which someone should be calling, because that's evidently
what you call if you are a program playing a video and you want to
inhibit the screen locker. But the unknown someone was delinquent and
it wasn't what I needed for this problem.
Then I moved on to the /MainApplication object and found
method void org.kde.KApplication.reparseConfiguration()
which wasn't quite what I was looking for either, but it might do: I
could perhaps modify the configuration and then invoke this method. I
dimly remembered that KDE keeps configuration files under
$HOME/.kde , so I ls -la -ed that and quickly found
share/config/kscreensaverrc , which looked plausible from the
outside, and more plausible when I saw what was in it:
Enabled=True
Timeout=60
among other things. I hand-edited the file to change the 60 to
243 , ran
qdbus org.freedesktop.ScreenSaver /MainApplication reparseConfiguration
and then opened up the System Settings app. Sure enough, the System
Settings app now reported that the lock timeout setting was “4
minutes”. And changing Enabled=True to Enabled=False and back
made the System Settings app report that the locker was enabled or
disabled.
The answer
So the script I wanted turned out to be:
timeout=${1:-3600}
perl -i -lpe 's/^Enabled=.*/Enabled=False/' $HOME/.kde/share/config/kscreensaverrc
qdbus org.freedesktop.ScreenSaver /MainApplication reparseConfiguration
sleep $timeout
perl -i -lpe 's/^Enabled=.*/Enabled=True/' $HOME/.kde/share/config/kscreensaverrc
qdbus org.freedesktop.ScreenSaver /MainApplication reparseConfiguration
Problem solved, but as so often happens, the journey was more
important than the destination.
I am greatly looking forward to exploring the D-Bus hierarchy and
sending all sorts of inappropriate messages to the wrong objects.
Just before he gets his ass kicked by Saruman, that insufferable
know-it-all Gandalf says “He who breaks a thing to find out what it is
has left the path of wisdom.” If I had been Saruman, I would have
kicked his ass at that point too.
Addendum
Right after I posted this, I started watching Netflix. The screen
locker cut in after sixty seconds. “Aha!” I said. “I'll run my new
script!”
I did, and went back to watching. Sixty seconds later, the screen
locker cut in again. My script doesn't work! The System Settings app
says the locker has been disabled, but it's mistaken. Probably it's only
reporting the contents of the configuration file that I edited, and
the secret sauce is still missing. The System Settings app does
something to update the state of the locker when I click that “Apply”
button, and I thought that my qdbus command was doing the same
thing, but it seems that it isn't.
I'll figure this out, but maybe not today. Good night all!
[ Addendum 20160728: I figured it out the next day ]
[ Addendum 20160729: It has come to my attention that there is
actually a program called xweasel . ]
[Other articles in category /Unix]
permanent link
A hack for getting the email address Git will use for a commit
Today I invented a pretty good hack.
Suppose I have branch topic checked out. It often happens that I want to
git push origin topic:mjd/topic
which pushes the topic branch to the origin repository, but on
origin it is named mjd/topic instead of topic . This is a good
practice when many people share the same repository. I wanted to write
a program that would do this automatically.
So the question arose, how should the program figure out the mjd
part? Almost any answer would be good here: use some selection of
environment variables, the current username, a hard-wired default, and
the local part of Git's user.email configuration setting, in some
order. Getting user.email is easy (git config get user.email ) but
it might not be set and then you get nothing. If you make a commit
but have no user.email , Git doesn't mind. It invents an address
somehow. I decided that I would like my program to to do exactly what
Git does when it makes a commit.
But what does Git use for the committer's email address if there is
no user.email set? This turns out to be complicated. It consults
several environment variables in some order, as I suggested before.
(It is documented in
git-commit-tree if you
are interested.) I did not want to duplicate Git's complicated
procedure, because it might change, and because duplicating code is a
sin. But there seemed to be no way to get Git to disgorge this value,
short of actually making a commit and examining it.
So I wrote this command, which makes a commit and examines it:
git log -1 --format=%ce $(git-commit-tree HEAD^{tree} < /dev/null)
This is extremely weird, but aside from that it seems to have no
concrete drawbacks. It is pure hack, but it is a hack that works
flawlessly.
What is going on here? First, the $(…) part:
git-commit-tree HEAD^{tree} < /dev/null
The git-commit-tree command is what git-commit uses to actually
create a commit. It takes a tree object, reads a commit message from
standard input, writes a new commit object, and prints its SHA1 hash
on standard output. Unlike git-commit , it doesn't modify the index
(git-commit would use git-write-tree to turn the index into a tree
object) and it doesn't change any of the refs (git-commit would
update the HEAD ref to point to the new commit.) It just creates
the commit.
Here we could use any tree, but the tree of the HEAD commit is
convenient, and HEAD^{tree} is its name. We supply an empty commit
message from /dev/null .
Then the outer command runs:
git log -1 --format=%ce $(…)
The $(…) part is replaced by the SHA1 hash of the commit we just
created with git-commit-tree . The -1 flag to git-log gets the
log information for just this one commit, and the --format=%ce tells
git-log to print out just the committer's email address, whatever it
is.
This is fast—nearly instantaneous—and cheap. It doesn't change the
state of the repository, except to write a new object, which typically
takes up 125 bytes. The new commit object is not attached to any refs
and so will be garbage collected in due course. You can do it in the
middle of a rebase. You can do it in the middle of a merge. You can
do it with a dirty index or a dirty working tree. It always works.
(Well, not quite. It will fail if run in an empty repository, because
there is no HEAD^{tree} yet. Probably there are some other
similarly obscure failure modes.)
I called the shortcut git-push program
git-pusho
but I dropped the email-address-finder into
git-get ,
which is my storehouse of weird “How do I find out X” tricks.
I wish my best work of the day had been a little bit more
significant, but I'll take what I can get.
[ Addendum:
Twitter user @shachaf has reminded me that the right way to do this is
git var GIT_COMMITTER_IDENT
which prints out something like
Mark Jason Dominus (陶敏修) <mjd@plover.com> 1469102546 -0400
which you can then parse. @shachaf also points out that a Stack
Overflow discussion of this very question contains
a comment suggesting the same weird hack! ]
[Other articles in category /prog]
permanent link
Surprising reasons to use a syntax-coloring editor
[ Danielle Sucher reminded me of this article I wrote in 1998, before I had a blog, and I thought I'd repatriate it here.
It should be interesting as a historical artifact, if nothing else.
Thanks Danielle! ]
I avoided syntax coloring for years, because it seemed like a pretty
stupid idea, and when I tried it, I didn't see any benefit. But
recently I gave it another try, with Ilya Zakharevich's `cperl-mode'
for Emacs. I discovered that I liked it a lot, but for surprising
reasons that I wasn't expecting.
I'm not trying to start an argument about whether syntax coloring is
good or bad. I've heard those arguments already and they bore me to
death. Also, I agree with most of the arguments about why syntax
coloring is a bad idea. So I'm not trying to argue one way or the
other; I'm just relating my experiences with syntax coloring. I used
to be someone who didn't like it, but I changed my mind.
When people argue about whether syntax coloring is a good idea or not,
they tend to pull out the same old arguments and dust them off. The
reasons I found for using syntax coloring were new to me; I'd never
seen anyone mention them before. So I thought maybe I'd post them here.
Syntax coloring is when the editor understands something about the
syntax of your program and displays different language constructs in
different fonts. For example, cperl-mode displays strings in
reddish brown, comments in a sort of brick color, declared variables
(in my ) in gold, builtin function names (defined ) in green,
subroutine names in blue, labels in teal, and keywords (like my and
foreach ) in purple.
The first thing that I noticed about this was that it was easier to
recognize what part of my program I was looking at, because each
screenful of the program had its own color signature. I found that I
was having an easier time remembering where I was or finding that
parts I was looking for when I scrolled around in the file. I wasn't
doing this consciously; I couldn't describe the color scheme any
particular part of the program was, but having red, gold, and purple
blotches all over made it easier to tell parts of the program apart.
The other surprise I got was that I was having more fun programming.
I felt better about my programs, and at the end of the day, I felt
better about the work I had done, just because I'd spent the day
looking at a scoop of rainbow sherbet instead of black and white. It
was just more cheerful to work with varicolored text than monochrome
text. The reason I had never noticed this before was that the other
coloring editors I used had ugly, drab color schemes. Ilya's scheme
won here by using many different hues.
I haven't found many of the other benefits that people say they get
from syntax coloring. For example, I can tell at a glance whether or
not I failed to close a string properly—unless the editor has
screwed up the syntax coloring, which it does often enough to ruin the
benefit for me. And the coloring also slows down the editor. But the
two benefits I've described more than outweigh the drawbacks for me.
Syntax coloring isn't a huge win, but it's definitely a win.
If there's a lesson to learn from this, I guess it's that it can be
valuable to revisit tools that you rejected, to see if you've changed
your mind. Nothing anyone said about it was persuasive to me, but
when I tried it I found that there were reasons to do it that nobody
had mentioned. Of course, these reasons might not be compelling for
anyone else.
Addenda 2016
Looking back on this from a distance of 18 years, I am struck by the
following thoughts:
Syntax highlighting used to make the editor really slow. You had to
make a real commitment to using it or not. I had forgotten about
that. Another victory for Moore’s law!
Programmers used to argue about it. Apparently programmers will
argue about anything, no matter how ridiculous. Well okay, this
is not a new observation. Anyway, this argument is now
finished. Whether people use it or not, they no longer find the
need to argue about it. This is a nice example that sometimes
these ridiculous arguments eventually go away.
I don't remember why I said that syntax highlighting “seemed like a
pretty stupid idea”, but I suspect that I was thinking that the
wrong things get highlighted. Highlighters usually highlight the
language keywords, because they're easy to recognize. But this
is like highlighting all the generic filler words in a
natural language text. The words you want to see are
exactly the opposite of what is typically highlighted.
Syntax highlighters should be highlighting the semantic content
like expression boundaries, implied parentheses, boolean
subexpressions, interpolated variables and other non-apparent
semantic features. I think there is probably a lot of interesting
work to be done here. Often you hear programmers say things like
“Oh, I didn't see the that the trailing comma was actually a
period.” That, in my opinion, is the kind of thing the syntax
highlighter should call out. How often have you heard someone say
“Oh, I didn't see that while there”?
I have been misspelling “arguments” as “argmuents” for at least 18
years.
[Other articles in category /prog]
permanent link
A simple but difficult arithmetic puzzle
Lately my kids have been interested in puzzles of this type: You are
given a sequence of four digits, say 1,2,3,4, and your job is to
combine them with ordinary arithmetic operations (+, -, ×, and ÷) in any order to
make a target number, typically 24. For example, with 1,2,3,4, you
can go with $$((1+2)+3)×4 = 24$$ or with $$4×((2×3)×1) = 24.$$
We were stumped trying to make 6,6,5,2 total 24, so I hacked up a
solver; then we felt a little foolish when we saw the solutions,
because it is not that hard. But in the course of testing the solver,
I found the most challenging puzzle of this type that I've ever seen.
It is:
Given 6,6,5,2, make 17.
There are no underhanded tricks. For example, you may not concatenate
2 and 5 to make 25; you may not say !!6÷6=1!! and !!5+2=7!! and
concatenate 1 and 7 to make !!17!!; you may not interpret the 17 as a
base 12 numeral, etc.
I hope to write a longer article about solvers in the next week or so.
[ Addendum 20170305: The next week or so, ha ha.
Anyway, here it is. ]
[Other articles in category /math]
permanent link
Addenda to recent articles 201607
Here are some notes on posts from the last couple of months
that I couldn't find better places for.
I wrote
a long article about tracking down a system bug.
At some point I determined that the problem was related to Perl, and
asked Frew Schmidt for advice.
He wrote up the details of his own investigation,
which pick up where mine ended. Check it out. I 100% endorse his
lament about ltrace .
There was
a Hacker News discussion about that article.
One participant asked a very pertinent question:
I read this, but seemed to skip over the part where he explains
why this changed suddenly, when the behavior was documented?
What changed to make the perl become capable whereas previously it
lacked the low port capability?
So far, we don't know! Frew told me recently that he thinks the
TMPDIR -losing has been going on for months and that whatever
precipitated my problem is something else.
In
my article on the Greek clock,
I guessed a method for calculating the (approximate) maximum length
of the day from the latitude: $$ A = 360 \text{ min}\cdot(1-\cos L).$$
Sean Santos of UCAR points out that this is inaccurate close to the
poles. For places like Philadelphia (40° latitude) it is pretty
close, but it fails completely for locations north of the Arctic
Circle. M. Santos advises instead:
$$ A = 360 \text{ min}\cdot \frac{2}{\pi}\cdot \sin^{-1}(\tan L\cdot
\tan\epsilon)$$
where ε is the axial tilt of the Earth,
approximately 23.4°. Observe that when !!L!! is above the Arctic
Circle (or below the Antarctic) we have
!!\tan L \cdot \tan \epsilon > 1!! (because
!!\frac1{\tan x} = \tan(90^\circ - x)!!)
so the arcsine is undefined, and we get no answer.
[Other articles in category /addenda]
permanent link
Don't tug on that, you never know what it might be attached to
This is a story about a very interesting bug that I tracked down
yesterday. It was causing a bad effect very far from where the bug
actually was.
emacsclient
The emacs text editor comes with a separate utility, called
emacsclient , which can communicate with the main editor process and
tell it to open files for editing. You have your main emacs
running. Then somewhere else you run the command
emacsclient some-files...
and it sends the main emacs a message that you want to edit
some-files . Emacs gets the message and pops up new windows for editing
those files. When you're done editing some-files you tell Emacs, by
typing C-# or something, it
it communicates back to emacsclient that the editing is done, and
emacsclient exits.
This was more important in the olden days when Emacs was big and
bloated and took a long time to start up. (They used to joke that
“Emacs” was an abbreviation for “Eight Megs And Constantly Swapping”.
Eight megs!) But even today it's still useful, say from shell scripts
that need to run an editor.
Here's the reason I was running it. I have a very nice shell script,
called also , that does something like this:
- Interpret command-line arguments as patterns
- Find files matching those patterns
- Present a menu of the files
- Wait for me to select files of interest
- Run
emacsclient on the selected files
It is essentially a wrapper around
menupick ,
a menu-picking utility I wrote which has seen use as a component of
several other tools.
I can type
also Wizard
in the shell and get a menu of the files related to the wizard, select
the ones I actually want to edit, and they show up in Emacs. This is
more convenient than using Emacs itself to find and open them. I use
it many times a day.
Or rather, I did until this week, when it suddenly stopped working.
Everything ran fine until the execution of emacsclient , which would
fail, saying:
emacsclient: can't find socket; have you started the server?
(A socket is a facility that enables interprocess communication, in
this case between emacs and emacsclient .)
This message is familiar. It usually means that I have forgotten to
tell Emacs to start listening for emacsclient , by running M-x
server-start . (I should have Emacs do this when it starts up, but I
don't. Why not? I'm not sure.) So the first time it happened I went
to Emacs and ran M-x server-start . Emacs announced that it had
started the server, so I reran also . And the same thing happened.
emacsclient: can't find socket; have you started the server?
Finding the socket
So the first question is: why can't emacsclient find the socket?
And this resolves naturally into two subquestions: where is the
socket, and where is emacsclient looking?
The second one is easily answered; I ran strace emacsclient (hi
Julia!) and saw that the last interesting thing emacsclient did
before emitting the error message was
stat("/mnt/tmp/emacs2017/server", 0x7ffd90ec4d40) = -1 ENOENT (No such file or directory)
which means it's looking for the socket at /mnt/tmp/emacs2017/server
but didn't find it there.
The question of where Emacs actually put the socket file was a little
trickier. I did not run Emacs under strace because I felt sure that
the output would be voluminous and it would be tedious to grovel over
it.
I don't exactly remember now how I figured this out, but I think now
that I probably made an educated guess, something like: emacsclient
is looking in /mnt/tmp ; this seems unusual. I would expect the
socket to be under /tmp . Maybe it is under /tmp ? So I looked
under /tmp and there it was, in /tmp/emacs2017/server :
srwx------ 1 mjd mjd 0 Jun 27 11:43 /tmp/emacs2017/server
(The s at the beginning there means that the file is a “Unix-domain
socket”. A socket is an endpoint for interprocess communication. The
most familiar sort is a TCP socket, which has a TCP address, and which
enables communication over the internet. But since ancient times Unix
has also supported Unix-domain sockets, which enable communication
between two processes on the same machine. Instead of TCP addresses,
such sockets are addressed using paths in the filesystem, in this case
/tmp/emacs2017/server . When the server creates such a socket, it
appears in the filesystem as a special type of file, as here.)
I confirmed that this was the correct file by typing M-x
server-force-delete in Emacs; this immediately caused
/tmp/emacs2017/server to disappear. Similarly M-x server-start
made it reappear.
Why the disagreement?
Now the question is: Why is emacsclient looking for the socket under
/mnt/tmp when Emacs is putting it in /tmp ? They used to
rendezvous properly; what has gone wrong? I recalled that there was
some environment variable for controlling where temporary files are
put, so I did
env | grep mnt
to see if anything relevant turned up. And sure enough there was:
TMPDIR=/mnt/tmp
When programs want to create tmporary files and directories, they normally do it in /tmp . But
if there is a TMPDIR setting, they use that directory instead. This
explained why emacsclient was looking for
/mnt/tmp/emacs2017/socket . And the explanation for why Emacs itself
was creating the socket in /tmp seemed clear: Emacs was failing to
honor the TMPDIR setting.
With this clear explanation in hand, I began to report the bug in
Emacs, using M-x report-emacs-bug . (The folks in the #emacs IRC
channel on Freenode suggested this. I had a bad
experience last time I tried
#emacs , and then people mocked me for even trying to get useful
information out of IRC. But this time it went pretty well.)
Emacs popped up a buffer with full version information and invited me
to write down the steps to reproduce the problem. So I wrote down
% export TMPDIR=/mnt/tmp
% emacs
and as I did that I ran those commands in the shell.
Then I wrote
In Emacs:
M-x getenv TMPDIR
(emacs claims there is no such variable)
and I did that in Emacs also. But instead of claiming there was no
such variable, Emacs cheerfully informed me that the value of TMPDIR
was /mnt/tmp .
(There is an important lesson here! To submit a bug report, you find
a minimal demonstration. But then you also try the minimal
demonstration exactly as you reported it. Because of what just
happened! Had I sent off that bug report, I would have wasted
everyone else's time, and even worse, I would have looked like a
fool.)
My minimal demonstration did not demonstrate. Something else was
going on.
Why no TMPDIR ?
This was a head-scratcher. All I could think of was that
emacsclient and Emacs were somehow getting different environments,
one with the TMPDIR setting and one without. Maybe I had run them
from different shells, and only one of the shells had the setting?
I got on a sidetrack at this point to find out why TMPDIR was set in
the first place; I didn't think I had set it. I looked for it in
/etc/profile , which is the default Bash startup instructions, but it
wasn't there. But I also noticed an /etc/profile.d which seemed
relevant. (I saw later that the /etc/profile contained instructions
to load everything under /etc/profile.d .) And when I grepped for
TMPDIR in the profile.d files, I found that it was being set by
/etc/profile.d/ziprecruiter_environment.sh , which the sysadmins had
installed. So that mystery at least was cleared up.
That got me on a second sidetrack, looking through our Git history for
recent changes involving TMPDIR . There weren't any, so that was a
dead end.
I was still puzzled about why Emacs sometimes got the TMPDIR setting
and sometimes not. That's when I realized that my original Emacs
process, the one that had failed to rendezvous with emacsclient ,
had not been started in the usual way. Instead of simply running
emacs , I had run
git re-edit
which invokes Git, which then runs
/home/mjd/bin/git-re-edit
which is a Perl program I wrote that does a bunch of stuff to figure
out which files I was editing recently and then execs emacs to edit
them some more. So there are several programs here that could be
tampering with the environment and removing the TMPDIR setting.
To more accurately point the finger of blame, I put some diagnostics
into the git-re-edit program to have it print out the value of
TMPDIR . Indeed, git-re-edit reported that TMPDIR was unset.
Clearly, the culprit was Git, which must have been removing TMPDIR
from the environment before invoking my Perl program.
Who is stripping the environment?
To confirm this conclusion, I created a tiny shell script,
/home/mjd/bin/git-env , which simply printed out the environment, and
then I ran git env , which tells Git to find git-env and run it.
If the environment it printed were to omit TMPDIR , I would know Git
was the culprit. But TMPDIR was in the output.
So I created a Perl version of git-env , called git-perlenv , which
did the same thing, and I ran it via git perlenv . And this time
TMPDIR was not in the output. I ran diff on the outputs of git
env and git perlenv and they were identical—except that git
perlenv was missing TMPDIR .
So it was Perl's fault! And I verified this by running perl
/home/mjd/bin/git-re-edit directly, without involving Git at all.
The diagnostics I had put in reported that TMPDIR was unset.
WTF Perl?
At this point I tried getting rid of get-re-edit itself, and ran the
one-line program
perl -le 'print $ENV{TMPDIR}'
which simply runs Perl and tells it to print out the value of the
TMPDIR environment variable. It should print /mnt/tmp , but instead
it printed the empty string. This is a smoking gun, and Perl no
longer has anywhere to hide.
The mystery is not cleared up, however. Why was Perl doing this?
Surely not a bug; someone else would have noticed such an obvious bug
sometime in the past 25 years. And it only failed for TMPDIR , not
for other variables. For example
FOO=bar perl -le 'print $ENV{FOO}'
printed out bar as one would expect. This was weird: how could
Perl's environment handling be broken for just the TMPDIR variable?
At this point I got Rik Signes and Frew Schmidt to look at it with
me. They confirmed that the problem was not in Perl generally, but
just in this Perl. Perl on other systems did not display this
behavior.
I looked in the output of perl -V , which says what version of Perl
you are using and which patches have been applied, and wasted a lot of
time looking into
CVE-2016-2381,
which seemed relevant. But it turned out to be a red herring.
Working around the problem, 1.
While all this was going on I was looking for a workaround. Finding
one is at least as important as actually tracking down the problem
because ultimately I am paid to do something other than figure out why
Perl is losing TMPDIR . Having a workaround in hand means that when
I get sick and tired of looking into the underlying problem I can
abandon it instantly instead of having to push onward.
The first workaround I found was to not use the Unix-domain socket.
Emacs has an option to use a TCP socket instead, which is useful on
systems that do not support Unix-domain sockets, such as non-Unix
systems. (I am told that some do still exist.)
You set the server-use-tcp variable to a true value, and when you
start the server, Emacs creates a TCP socket and writes a description
of it into a “server file”, usually ~/.emacs.d/server/server . Then
when you run emacsclient you tell it to connect to the socket that
is described in the file, with
emacsclient --server-file=~/.emacs.d/server/server
or by setting the EMACS_SERVER_FILE environment variable. I tried
this, and it worked, once I figured out the thing about
server-use-tcp and what a “server file” was. (I had misunderstood
at first, and thought that “server file” meant the Unix-domain socket
itself, and I tried to get emacsclient to use the right one by
setting EMACS_SERVER_FILE , which didn't work at all. The resulting
error message was obscure enough to lead me to IRC to ask about it.)
Working around the problem, 2.
I spent quite a while looking for an environment variable analogous to
EMACS_SERVER_FILE to tell emacsclient where the Unix-domain socket
was. But while there is a --socket-name command-line argument to
control this, there is inexplicably no environment variable. I hacked
my also command (responsible for running emacsclient ) to look for
an environment variable named EMACS_SERVER_SOCKET , and to pass its
value to emacsclient --socket-name if there was one. (It probably
would have been better to write a wrapper for emacsclient , but I
didn't.) Then I put
EMACS_SERVER_SOCKET=$TMPDIR/emacs$(id -u)/server
in my Bash profile, which effectively solved the problem. This set
EMACS_SERVER_SOCKET to /mnt/tmp/emacs2017/server whenever I
started a new shell. When I ran also it would notice the setting
and pass it along to emacsclient with --socket-name , to tell
emacsclient to look in the right place. Having set this up I could
forget all about the original problem if I wanted to.
But but but WHY?
But why was Perl removing TMPDIR from the environment? I didn't
figure out the answer to this; Frew took it to the #p5p IRC channel
on perl.org , where the answer was eventually tracked down by Matthew
Horsfall and Zefrem.
The answer turned out to be quite subtle. One of the classic attacks
that can be mounted against a process with elevated privileges is as
follows. Suppose you know that the program is going to write to a
temporary file. So you set TMPDIR beforehand and trick it into
writing in the wrong place, possibly overwriting or destroying
something important.
When a program is loaded into a process, the dynamic loader does the
loading. To protect against this attack, the loader checks to see if
the program it is going to run has elevated privileges, say because it
is setuid, and if so it sanitizes the process’ environment to prevent
the attack. Among other things, it removes TMPDIR from the
environment.
I hadn't thought of exactly this, but I had thought of something like
it: If Perl detects that it is running setuid, it enables
a secure mode which, among other things, sanitizes the environment.
For example, it ignores the PERL5LIB environment variable that
normally tells it where to look for loadable modules, and instead
loads modules only from a few compiled-in trustworthy directories. I
had checked early on to see if this was causing the TMPDIR problem,
but the perl executable was not setuid and Perl was not running in
secure mode.
But Linux supports a feature called “capabilities”, which is a sort of
partial superuser privilege. You can give a program some of the
superuser's capabilities without giving away the keys to the whole
kingdom. Our systems were configured to give perl one extra
capability, of binding to low-numbered TCP ports, which is normally
permitted only to the superuser. And when the dynamic loader ran
perl , it saw this additional capability and removed TMPDIR from
the environment for safety.
This is why Emacs had the TMPDIR setting when run from the command
line, but not when run via git-re-edit .
Until this came up, I had not even been aware that the “capabilities”
feature existed.
A red herring
There was one more delightful confusion on the way to this happy
ending. When Frew found out that it was just the Perl on my
development machine that was misbehaving, he tried logging into his
own, nearly identical development machine to see if it misbehaved in
the same way. It did, but when he ran a system update to update Perl,
the problem went away. He told me this would fix the problem on my
machine. But I reported that I had updated my system a few hours
before, so there was nothing to update!
The elevated capabilities theory explained this also. When Frew
updated his system, the new Perl was installed without the elevated
capability feature, so the dynamic loader did not remove TMPDIR from
the environment.
When I had updated my system earlier, the same thing happened. But
as soon as the update was complete, I reloaded my system configuration, which
reinstated the capability setting. Frew hadn't done this.
Summary
- The system configuration gave
perl a special capability
- so the dynamic loader sanitized its environment
- so that when
perl ran emacs ,
- the Emacs process didn't have the
TMPDIR environment setting
- which caused Emacs to create its listening socket in the usual place
- but because
emacsclient did get the setting, it looked in the wrong place
Conclusion
This computer stuff is amazingly complicated. I don't know how anyone
gets anything done.
[ Addendum 20160709: Frew Schmidt has written up the same
incident,
but covers different ground than I do. ]
[ Addendum 20160709: A Hacker News comment asks what changed to cause
the problem? Why was Perl losing TMPDIR this week but not the week
before? Frew and I don't know! ]
[Other articles in category /tech]
permanent link
|