Elegance of Reason

Thursday, December 15, 2022

Problem Exists Between Keyboard ...

 Today I started getting "Sorry, try again" from sudo after a supposedly unrelated Fedora update. After some PAM debug fun, the problem turned out to be ... the keyboard. Old laptop, one of the keys became fiddly.

Sunday, August 28, 2016

Surprisingly, this is still functional.

Monday, June 19, 2006

The Good, the Bad and the Ugly, part 5

Last time, reporting about errno brought the example code to new depths of ugliness. It is about time to introduce some relief.

The most obvious target is the repeated use (abuse ?) of fprintf, which can be addressed by using a simple wrapper function. Even the simplest function, however, introduces a new word in the vocabulary used to describe the solution to the problem implemented by the program - to describe it to a fellow programmer (which might be your future self), that is: the compiler is just as happy with the code as it stands. In other words, each function introduces an abstraction and as such it is essential to pick its name judiciously, so that it is not always necessary to reach for the actual source of the function in order to understand what it does.


/* Report an error on stderr */
void err_printf(const char *format, ...) {
va_list args;

va_start(args, format);
(void) vfprintf(stderr, format, args);
va_end(args);
}

Now, the somewhat verbose err_printf() might not appeal old UNIX hands, but this is my best effort at conveying what the function is supposed to do. I once read that the inability to come up with a name describing what a function does is a telltale sign that said function is attempting to do too many things at once; hopefully, the above function does not.

In doing its job, err_printf() embeds a decision: we are not interested in the return value of vfprintf, which is documented to be the same value that would be returned by the equivalent fprintf. In other words, the caller will get no information about the success or failure of the function; as briefly suggested last time, should he choose to invoke it, it will either be because he's genuinely uninterested in the outcome, or because he's confident that whatever the outcome subsequent execution of the program will satisfy his requirements.

Please note the above does not tell the reader of a non trivial fragment of code using err_printf() which of the above was the actual intent, i.e. if the programmer was merely uninterested in the outcome or actually confident in its non-relevance. The latter gets problematic if code maintenance adds code afterwards which depends on the successful execution of err_printf, such as a followup message with more details about the problem.

However, even by itself, err_printf brings some measurable improvement to the example code:


/*
popen-test: experiment with popen(), Part 5.

Copyright © 2006 Davide Bolcioni <dbolcion@libero.it>

Distributed under the GPL,
see http://www.gnu.org/copyleft/gpl.html.

*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>

void err_printf(const char* format, ...)
{
va_list args;

va_start(args, format);
(void) vfprintf(stderr, format, args);
va_end(args);
}

int main(int argc, char* argv[])
{
int rc = EXIT_FAILURE; /* Optimist */

if (argc > 1) {
FILE* sink;

if (fflush(NULL) != EOF) { /* all open files */
sink = popen(argv[1], "w");

if (sink) {
int status;
int i;

/* NB: deliberately allow to test for no output sent */

for (i = 2; i < argc; i++) {
if (fputs(argv[i], sink) == EOF) {
err_printf("%s: fputs(\"%s\") failed: ",
argv[0], argv[i]);
perror("");
break;
}
}

status = pclose(sink);

/* The return value is funny */

if (status == -1) {
err_printf("%s: pclose() failed: ",
argv[0]);
perror("");
}
else if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
err_printf("%s: child did an exit(%d).\n",
argv[0], rc);
}
else if (WIFSIGNALED(status)) {
err_printf("%s: child terminated by signal %d.\n",
argv[0], WTERMSIG(status));
}
else if (WIFSTOPPED(status)) {
err_printf("%s: child stopped with signal %d.\n",
argv[0], WSTOPSIG(status));
}
else {
err_printf(
"%s: I don't think we're in Kansas anymore, Toto.\n",
argv[0]
);
}
}
else {
err_printf("%s: popen(\"%s\") failed: ",
argv[0], argv[1]);
perror("");
}
}
else {
err_printf("%s: fflush(NULL) failed: ",
argv[0]);
perror("");
}
}
else {
err_printf("Usage: %s <command> [<arg>]...\n",
argv[0]);
}

return rc;
}

/*
vim:sw=2:nowrap
*/


The more observant among you might notice that the above code has two problems: it falls afoul of the "what happens after" syndrome discussed above


...

else {
err_printf("%s: popen(\"%s\") failed: ",
argv[0], argv[1]);
perror("");
}

...


and might trash errno within err_printf(). This second problem is easily fixed1:


/* Report an error on stderr */
void err_printf(const char *format, ...) {
int saved_errno = errno;
va_list args;


va_start(args, format);
(void) vfprintf(stderr, format, args);
va_end(args);
errno = saved_errno;
}

which compares favorably against messing around with saving and restoring errno all over the program. It might be tempting to add perror() to the mix also:


/* Report an error on stderr */
void err_printf(const char *format, ...) {
int saved_errno = errno;
va_list args;


va_start(args, format);
(void) vfprintf(stderr, format, args);
va_end(args);

if (saved_errno) {
errno = saved_errno;
perror("");
}

errno = saved_errno;
}

Since it's not exactly clear what perror() is going to print in case of no error, the above code studiously avoids to trigger that. The next installment will attempt to discuss if the above is really an improvement.

Note [1]: as long as err_printf() and all its clients are compiled with the same threading flags.

Monday, May 15, 2006

The Good, the Bad and the Ugly, part 4

If you thought the code in part 3 was ugly, this installment will make you reconsider: we'll discuss the use of errno.

Apparently, there is little to say about errno; when a function returns failure, it sets the global variable1 errno to some numeric value which is supposed to give more details about what went wrong. When it returns success, you should not look at the value of errno because it might have changed anyway. Suppose we write a mightily uninteresting frobble() function:


/* The frobble() function returns 0 on success and -1 on failure */
int frobble() {
return -1;
}

Now, frobble() is documented to fail but there is no sign of it affecting errno in any way; the handling of errno is a convention, which dates back to the beginnings of C and UNIX, so functions have to be explicitly written to follow this convention and should be documented accordingly: system calls and library functions often do, but the only way to tell is to read the documentation.

The reason we're discussing errno lies in a problem with our toy program. Suppose we had the following run:


[db@centaur ~] ./popen-test /bin/true
[db@centaur ~] ./popen-test: popen("/bin/true") failed.

We get no clue about the reason which caused popen() to fail; whatever information errno might have conveyed is not made available. All too often, when programs fail they do not bother to tell you why, which makes the life of your system administrator friends either very simple (just blame you, the developer) or very complicated.

To rectify this, our task is conceptually simple: whenever we invoke a function which sets errno on failure, just make sure its value is included in the error message. Since the value is just an integer, which is not very user-friendly, we might want to carry this a step further and provide some kind of explanatory message using perror() (we'll get more sophisticated later). Note that we're not referencing errno in our code, just invoking perror().


/*
popen-test: experiment with popen(), Part 4.

Copyright © 2006 Davide Bolcioni <dbolcion@libero.it>

Distributed under the GPL,
see http://www.gnu.org/copyleft/gpl.html.

*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
int rc = EXIT_FAILURE; /* Optimist */

if (argc > 1) {
FILE* sink;

if (fflush(NULL) != EOF) { /* all open files */
sink = popen(argv[1], "w");

if (sink) {
int status;
int i;

/* NB: deliberately allow to test for no output sent */

for (i = 2; i < argc; i++) {
(void)fputs(argv[i], sink);
}

status = pclose(sink);

/* The return value is funny */

if (status == -1) {
(void)fprintf(stderr,
"%s: pclose() failed: ",
argv[0]);
perror("");
}
else if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
(void)fprintf(stderr,
"%s: child did an exit(%d).\n",
argv[0], rc);
}
else if (WIFSIGNALED(status)) {
(void)fprintf(stderr,
"%s: child terminated by signal %d.\n",
argv[0], WTERMSIG(status));
}
else if (WIFSTOPPED(status)) {
(void)fprintf(stderr,
"%s: child stopped with signal %d.\n",
argv[0], WSTOPSIG(status));
}
else {
(void)fprintf(stderr,
"%s: I don't think we're in Kansas anymore, Toto.\n",
argv[0]);
}
}
else {
(void)fprintf(stderr,
"%s: popen(\"%s\") failed: ",
argv[0], argv[1]);
perror("");
}
}
else {
(void)fprintf(stderr,
"%s: fflush(NULL) failed: ",
argv[0]);
perror("");
}
}
else {
(void)fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]);
}

return rc;
}

/*
vim:sw=2:nowrap
*/


The more observant among you might notice that we're blindly ignoring failures, and the attendant value of errno, resulting from the invocations of fputs() in the for loop. This illustrates why casting away error handling to void is slightly better than silently ignoring error conditions: it is easier to spot even after some time. In the code above, we should probably leave the loop as soon as the first fputs() fails and report the value of errno:


...

/* NB: deliberately allow to test for no output sent */

for (i = 2; i < argc; i++) {
if (fputs(argv[i], sink) == EOF) {
int errno_from_fputs = errno;
(void)fprintf(stderr, "%s: fputs(\"%s\") failed: ",
argv[0],
argv[i]);
errno = errno_from_fputs;
perror("");
break;
}
}

status = pclose(sink);

...


As you can see, in ugliness contests global variables such as errno have a head start: because fprintf() might affect errno even when not failing, we have to save it and then restore it back for the use of perror().

Note that we had to change a few \n in our fprintf() format strings to a colon followed by a space, so that perror() output follows the message on the same line; if supplied an non-empty argument perror() would do just that on its own, but this would require us to format the message as a string, with all the attendant complications.

Next time, our toy program will attempt to climb back up from the pits of ugliness it sank to.

Note [1]: this is not entirely correct, because errno is documented as an lvalue and might be a macro which expands to some funny construct intended to carry over all the above in the world of POSIX threads without too much pain. For this reason, errno should be accessed only after explicitly including <errno.h> even if perror() in all likelyhood already includes it.

Sunday, February 26, 2006

The Good, the Bad and the Ugly, part 3

Making fprintf() to standard error an example of why not checking return codes is bad is admittedly somewhat contrived, but this installment is going to amend for that by addressing another problem in the original code.

Before calling popen(), you may notice, the code invokes (void)fflush(NULL) - ignoring the result. The reason for this is somewhat involved, but easily explained: C standard I/O is buffered, so you might output a few characters, so few that they would sit in your stdout buffer waiting for more, invoke through popen() a program which sends output to standard output on its own, and this output would appear before the output your program produced before the call, which would be still sitting in its buffer. Calling fflush(NULL) flushes all open streams, so it's a kind of blanket prevention for the problem with output; when reading from standard input and invoking a program which itself reads from standard input (an input filter), the problem gets harder and will not be discussed here, and the same goes for similar problems associated with file descriptors.

You might wonder why popen() does not perform the equivalent operation by itself; the only reason I can offer you is that the standard says it does not need to. In a few specific cases, such as ours by the way, fflush() is superfluous: at the point in code where it's called, no output has been produced yet so no output could possibly be sitting in a buffer - but in this case fflush() should complete with little fuss anyway.

The real reason, in my opinion, is not visible under the microscope we're using to dissect this toy program, because it's much larger than that. In real code you don't call fflush(NULL) and ignore the result; an error would mean that some data that you thought you output might have failed to make it from the buffer to its intended destination. Can you say data loss ? I thought you could.

In real code, we would track each stream and would know within our code whether it needs to be flushed and, most important of all, what to do in case calling fflush() on it failed; this might include charging along into popen(), but chances are you would want to do more than that, and fflush(NULL) does not even tell you which stream had a problem, nor can help you address the case when more than one had a problem, and they were not the same problem.

In our toy program, fflush(NULL) should not do anything, much less fail. In part 2, two strategies for handling "should not happen" circumstances have been shown: cast to void or assert(). Note that our toy program already handles the case of popen() not providing a stream explicitly; the focus here is on handling the "should not happen" case instead.

A cast to void results in the program charging on
as if the call succeeded (not quite, in fact, as we'll discover when discussing what happens to errno). The writer is making a statement to the effect that

  • there is no interest in any of the circumstances reported

  • any reported circumstance does not invalidate the satisfactory operation of the code which follows

and while the first is entirely within the writer's judgement, the second inevitably includes a dose of wishful thinking, unless the definition of "satisfactory operation" is very accomodating.

The above paragraph should not be construed as banning the casting approach altogether; our toy program from part 2, for example, invokes fprintf() at most once in its operation, and then exits. If the invocation fails, there is little we can do (writing an error message just failed, after all) so carrying on as if the problem did not occur is appropriate: there is no interest in the circumstance, and a fair amount of confidence that it will not impact the program exiting shortly thereafter.

In the case of fflush(NULL), however, there is no such confidence that popen() will be guaranteed to function as advertised; on the contrary, such an unlikely failure would suggest nasty problems with the execution environment, so carrying along as if nothing happened does not cut it. The acknowledged intent of assert(), however, is guarding against inconsistencies in "our" code, not in the execution environment: on failure assert() aborts execution, which is an operation tied to the execution environment if there is one. So this is a circumstance that must be handled explicitly:


/*
popen-test: experiment with popen(), Part 3.

Copyright © 2006 Davide Bolcioni <dbolcion@libero.it>

Distributed under the GPL,
see http://www.gnu.org/copyleft/gpl.html.

*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
int rc = EXIT_FAILURE; /* Optimist */

if (argc > 1) {
FILE* sink;

if (fflush(NULL) != EOF) { /* all open files */
sink = popen(argv[1], "w");

if (sink) {
int status;
int i;

/* NB: deliberately allow to test for no output sent */

for (i = 2; i < argc; i++) {
(void)fputs(argv[i], sink);
}

status = pclose(sink);

/* The return value is funny */

if (status == -1) {
(void)fprintf(stderr,
"%s: pclose() failed.\n",
argv[0]);
}
else if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
(void)fprintf(stderr,
"%s: child did an exit(%d).\n",
argv[0], rc);
}
else if (WIFSIGNALED(status)) {
(void)fprintf(stderr,
"%s: child terminated by signal %d.\n",
argv[0], WTERMSIG(status));
}
else if (WIFSTOPPED(status)) {
(void)fprintf(stderr,
"%s: child stopped with signal %d.\n",
argv[0], WSTOPSIG(status));
}
else {
(void)fprintf(stderr,
"%s: I don't think we're in Kansas anymore, Toto.\n",
argv[0]);
}
}
else {
(void)fprintf(stderr,
"%s: popen(\"%s\") failed.\n",
argv[0], argv[1]);
}
}
else {
(void)fprintf(stderr,
"%s: fflush(NULL) failed.\n",
argv[0]);
}
}
else {
(void)fprintf(stderr,
"Usage: %s <command> [<arg>]...\n", argv[0]);
}

return rc;
}

/*
vim:sw=2:nowrap
*/


The code gets uglier still, but at least it's handling the case when unforeseen external occurrences prevent it from proceeding further. One cause of badness, ignoring the result of functions called for their side effects, has been addressed; in the next installment we'll have a look at the handling of errno and at programs that don't tell you why they fail.

Sunday, January 29, 2006

What Was Old is New Again

I don't know if blogs are going to kill Usenet, but today even I got to realize that the overlap is significant - better late than never, I guess. Usenet was network news, and you subscribed to the groups carrying topics of interest in order to get informed of novel developments.

Now, one of my interests is programming language design and evolution, and today's news came from blogs: a presentation about speculations about the future of Java in an otherwise unrelated posting over at The Cliffs of Inanity, and a very promising concept from none other than Bjarne Stroustrup himself from the blog of Carlo Pescio. This was the kind of news that was usually found on Usenet, possibly buried in a thread flaming one language over the other; this was the kind of news that made Usenet an useful source of news.

Now, don't get me wrong: Usenet had a number of cool features, and news dissemination was only one of them; for example, the news might be actually somewhat ... old, but it was brought up while discussing a topic of interest and often to illustrate a point, giving it context and depth; in the above cases, for example, the link about Java lacks this, but there is at least a glimmer of hope that Pescio will write about Bjarne's work in depth at a later date.

Saturday, January 28, 2006

Why the Anti GNU Movement ?

Following a link from a news site, I found the Anti GNU Movement and read the two posts there. The author, apparently, wants to convince fellow programmers that they should not release code under the GPL. Since the code I post is usually under such license, I feel compelled to state my reasons.

For context, please note that I fail to be incensed, one way or the other, at "communism". I live in what is considered a traditionally "communist" region of Italy, yet I used to vote against communism because of the chance it might somehow help the Soviet Union; when that risk went away, I lost interest. I thought the same should happen to most people, except maybe a few extremists, but over the following years I kept noticing that communism had became a recurrent scare word.

At first guess, I believe this is because a number of people who enjoy some degree of power over others (a communist might use "employer" and "workers" as examples, but this is so 20th century) would lose such power should those who are subjected to such power make good of their numbers and together oppose its exercise. The notion that such concerted effort has often a leader, which becomes the one wielding power, is of no comfort to those who risk losing whatever power they have; and losing something you have is an undisputed source of fear.

Having something to lose would explain why some software companies actively oppose the GPL. Microsoft, for example, is probably afraid of losing the control it enjoys over its desktop users, and possibly acrimonious because its attempt to gain control of the server failed; there's no indication that Microsoft is losing money under any shape or form, so it's not about money, it's about control.

However, although Microsoft got the ball rolling, they're not alone in saying that the GPL is communism; a number of voices over the Internet sing to the same tune, and the above blog is but one.

Unless this is another case of astroturfing, one would be tempted to think that some opponents of the GPL, especially among small companies and professional freelancers, actually resent the fact that the GPL makes building something they can sell on top of the work of others, without giving neither money nor code back, riskier than they would like.

Yet, opponents of the GPL include, for example, Alexander Terekhov, whose postings on subjects such as POSIX thread programming make it hard to believe he would need to sink that low. I cannot dismiss the possibility, therefore, that opposition to the GPL may be borne of purely ideological reasons; after all, it is a document where ideology plays a substantial role.

As you might have noticed, this author has not stated yet his reasons for using the GPL; for one, unlike the above, I am not at risk of losing something from doing so - I fail to see how I would incur any inconvenience should Microsoft lose control of the desktop, for example. Furthermore, I cannot help but regard those who would like to exploit somebody else's GPL code without giving back as parasites, so any step taken to hinder such activities looks to me as the moral equivalent of basic hygiene. Last, the activities of others releasing their code under the GPL provided me with a computing environment which I find more desiderable than other alternatives, so I have reason to believe that in doing the same I might be able to contribute back, thus furthering my own interests. It's as simple as that.