Tuesday, August 12, 2008

man-pages-3.07 is released

I've uploaded man-pages-3.07 into the release directory (or view the online pages). Notable changes in man-pages-3.07 are:

  • A new move_pages(2) page, written by Christoph Lameter, documents the move_pages() system call. This page was formerly part of the numactl package, but has been revised and moved into man-pages (its natural home, since it is a kernel interface).
  • A new clock_getcpuclockid(3) page describes the clock_getcpuclockid() library function.
  • A new udplite(7) page, written by Gerrit Renker, documents the Linux implementation (since kernel 2.6.20) of the UDP-Lite transport layer protocol.
  • The proc(5) man page adds a description of the /proc/PID/numa_maps file.
  • Various updates and improvements by Lee Schermerhorn for the mbind(2), get_mempolicy(2), and set_mempolicy(2) pages.
  • Following on from last week's big update to the math man pages, there are a few more changes to these pages. Most notably, where error-reporting details have differed (from glibc 2.8) in earlier versions of glibc, the differences have been noted in the man pages. Currently, the details have been extended back until glibc 2.3.2, but I hope to extend them back further, when I can get test results. If you want to help, look here.
  • On 24 July, the Governing Board of The Open Group approved the 2008 revisions of POSIX.1. Among other things, POSIX.1-2008 marks some previously specified functions as obsolete, and drops the specifications of some other functions altogether. The manual pages have been updated to reflect all of these changes. (The standard should be published in final form after approval by the IEEE, but in the meantime you can get access to the draft by joining the Austin group.)
  • VERSIONS sections have been added to the man pages of many library functions to indicate the glibc version where the function first appeared.

Grünegg Fenster

Friday, August 8, 2008

Translating man-pages

Update, 2020-06-20: I've maintained the post below mainly for historical interest. However things have moved along quite a bit in 12 years, rendering various of my suggestions obsolete. If you are thinking about translating manual pages, see the manpages-I10n project, which oversees translations into several languages and employs a range of useful tooling for translation.

Lately, I've gotten a few requests for information about how to translate man-pages into other languages. First off, I should say that I have never translated man-pages. But I have communicated with a few people who do. So these are my current thoughts... Do you really want to do this? Before you answer this, consider the following:
  • man-pages contains the documentation of the Linux and glibc programming APIs (i.e., pages in Sections 2, 3, 4, 5, and 7). Is this the set of man pages that your group of language speakers most need? If, for example, you are more interested in translating pages for end users, then you might want instead to translate pages in the coreutils package. (More generally, if you want to find out which package a particular man page belongs to, take a look here.)
  • How big is the target audience? Your target audience is primarily programmers. What proportion of them aren't able to read English well enough to read man pages, and therefore would benefit from a translation? Is that group big enough to warrant the effort of a translation? Or is there perhaps a better place where you can invest your time in working on Linux?
  • How much time do you have? There are currently around 850 pages in man-pages, amounting to perhaps 2000 pages of printed text. My guess is that this amounts to one to two person years of translation work. In other words, you'll need to have a team of translators, if you intend to complete the translation in any reasonable time.
  • What is your longer term commitment? man-pages is a moving target: starting a couple of months ago, I'm now working full time on man-pages, and I make a release every week or so. The French translator estimates that there is around two days' work for him translating each release. Now, I may not be working full time on man-pages forever, and therefore the required translation effort may decrease some day, but the point remains that there is a significant ongoing effort required to keep a translation up to date and useful.

The size of the translation effort should not be underestimated. It is because it is so large that to date there has been only one complete and up-to-date translation: the French translation. (For a while, there was a fairly full German translation, but it seems to have languished for a few years now.) The state of the French translation has largely been down to the extraordinary work of two people: Christophe Blaess, and more recently, Alain Portal. (In fact, there are nowadays two French translations which cooperate to some extent: the Debian distribution has a team doing a French translation of man-pages.) But nowadays even the French translator(s) have started to feel the strain resulting from the recent increase in my output.

If you decide you really want to do a translation (and think very carefully before you do decide that!), then I have a few thoughts on how you go about it.

Tools: I have no real recommendations here (since I never translated man-pages). But it's worth mentioning that the Debian French translators use po4a, and see it as very beneficial for their work, especially for facilitating the work of a team of translators.

Other than that, I'd say that you need to:

  • Estimate the time required to translate the 850 pages in man-pages, and decide if you have the necessary number translators who have sufficient time to complete the work.
  • Divide the work up so that your translators can work independently on translations. I suggest you divide the pages up into small, related parcels. For example, the POSIX message queue pages (mq_*) could be a parcel translated by a single translator, or the math man pages could be a parcel translated by a single translator, etc.
  • Come up with a review plan, so that each translation by one member of your team is reviewed by at least one other member.
  • Devise a glossary of terminology, so that you all translate English technical terms ("e.g., shared memory segment") into the same terms in the target language.
  • Plan for ongoing maintenance, so that as the English man pages are updated, then the translated pages are also updated. Don't underestimate the amount of this work!

My suggestion is that if you go forward with a translation project, then:

  • Pick a particular man-pages release -- let's say man-pages-3.x -- and translate all of the pages in that version.
  • When that is completed, you can then update your translation with all of the changes that have occurred in the English original since release man-pages-3.x. I keep fairly detailed changelogs which should assist you during this phase of the work.

I suggest doing things this way since I estimate that trying to do a translation while simultaneously trying to keep up with changes in already translated pages would just prove too difficult. You might decide otherwise.

And finally... did I mention that you should think long and hard before embarking on a translation of man-pages?

[12 Aug 08: minor updates, to point out exactly which sections are in man-pages, and to suggest more appropriate pages to translate, if targeting end users.]

Wednesday, August 6, 2008

Help request: testing of math function error reporting

As I mentioned in my previous post, as of man-pages-3.06, the math man pages now describe the error-reporting behavior of the math functions as at glibc 2.8. I'd like to extend those descriptions to cover differences in older glibc versions. In order to do that, I've written some scripts to check the error-reporting behavior of the math functions, and I'd like to run them on as many different versions of glibc as possible.

If you'd like to help, and you have an x86 system with an older glibc (look at the version number in the first line of output produced by the command /lib/libc.so.6), run the script in this tarball (see the README file inside the tarball for details), and send me the resulting log file (email to mtk.manpages AT gmail.com).

Updated, 2012- 03-06: Fix link to tarball

Math functions and error reporting

Math functions are different from most other library functions in the kinds of errors that they report, and in the way that they report errors. Broadly speaking, a math function can fail for one of the following reasons:

  • Domain error: an argument to the function was outside the range for which the function was defined. For example, the call sqrt(-1.0) gives a domain error because a negative number does not have (real) square root. When a domain error occurs, a math function typicall returns a NaN (not-a-number).
  • Pole error: the function result is an exact infinity. For example log(0.0) is negative infinity. When a pole error occurs, most math functions return the floating-point representation of positive or negative infinity, as appropriate (i.e., HUGE_VAL or -HUGE_VAL for functions returning a double).
  • Range error (overflow): an overflow occurs if the function result is too large to be represented as a floating-point number. For example, exp(1e10) produces a number too large to represent in a double. When an overflow occurs, most math functions return the floating-point representation of positive or negative infinity, as appropriate (i.e., HUGE_VAL or -HUGE_VAL for functions returning a double).
  • Range error (underflow): an underflow occurs if the function result is so small that it can't be represented as a (normalized) floating-point number. For example, exp(-1e10) produces a number too large to represent in a double. When an underflow occurs, a math function usually either returns a (signed) zero, or a subnormal value, as appropriate.

(More details can be found in the math_error(7) man page.)

Many library functions report an error by returning a NULL pointer or an integer -1. Neither of these mechanisms would be suitable for math functions: these functions usually return a floating-point value, and -1 is in many cases a valid successful return. For this reasons, the C99 and POSIX.1-2001 standards define two other mechanisms by which math functions can report errors.

The first of the error-reporting mechanisms is to use the traditional errno variable. We set the errno to zero before the call, and if it has a non-zero value after the call, then an error occurred. On error, errno is set as follows:

  • Domain error: EDOM

  • Pole error: ERANGE

  • Overflow: ERANGE

  • Underflow: ERANGE
(These settings do of course make it hard to distinguish the last three types of errors.)

The other error-reporting mechanism is exceptions. For each of the errors described above, the system raises an exception, and the fetestexcept() library function can be used to check whether an exception occurred. In order to use this mechanism we do the following:

  1. Call feclearexcept(FE_ALL_EXCEPT) to clear any existing exceptions.

  2. Call the math library function.

  3. Call fetestexcept(FE_INVALID FE_DIVBYZERO FE_OVERFLOW FE_UNDERFLOW).

If the math function was successful, then fetestexcept() returns 0. If an error occurred while calling the math function, then fetestexcept() returns a bit mask indicating the error. In this bit mask, exactly one of FE_INVALID, FE_DIVBYZERO, FE_OVERFLOW, or FE_UNDERFLOW will be set. The exceptions raised for each error are:

  • Domain error: invalid exception (FE_INVALID)

  • Pole error: divide-by-zero exception (FE_DIVBYZERO)

  • Overflow: overflow exception (FE_OVERFLOW)

  • Underflow: underflow exception (FE_UNDERFLOW)

C99 and POSIX.1-2001 require an implementation to support at least one of the error-reporting mechanisms for all math functions, and allow both to be supported. The standards specify an identifier, math_errhandling, that an implementation should set to indicate which mechanisms are supported. If (math_errhandling & MATH_ERRNO) is non-zero, then errno is set to indicate errors. If (math_errhandling & MATH_EXCEPT) is non-zero ,then exceptions are raised on errors.

The CONFORMANCE file in the glibc sources has long explained that:

Implementing MATH_ERRNO, MATH_ERREXCEPT and math_errhandling in needs compiler support: see
http://sources.redhat.com/ml/libc-hacker/2000-06/msg00008.html
http://sources.redhat.com/ml/libc-hacker/2000-06/msg00014.html
http://sources.redhat.com/ml/libc-hacker/2000-06/msg00015.html

But to date this support has not arrived. In any case, this support is a somewhat moot point, since it transpires that neither of the mechanisms is supported by all of the math functions in glibc: most (but not all) support exceptions, many support both exceptions and errno, a few support errno but not exceptions, and one or two functions support neither mechanism. To make things even worse, the man pages didn't fully and correctly describe the details for each math function. Since man-pages-3.06, the details should now be accurate, at least for glibc 2.8.

Ideally, all of the glibc math functions would support both mechanisms, so that programs that depend on either mechanism could be happily ported to Linux. With that idea in mind, I went through and tested the error-reporting behavior for each math function, and filed a series of bug reports that document deviations from that ideal.

In order to get an overview, the table below summarizes the situation for all of the math functions as at glibc 2.8. The third and fourth columns indicate whether errno is correctly set and an exception is raised for each error case.































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































FunctionExpected errorerrno set correctly?Exception correctly raised?Notes
acos(+-inf)domainyy-
acosh(x<1)domainyy-
asin(+-inf)domainyy-
asinh()---No errors occur
atan()---No errors occur
atan2()---No errors occur
atanh(+-1)polenyerrno is set to EDOM (should be ERANGE)
atanh(x>1)domainyy-
cbrt()---No errors occur
ceil()---No errors occur
cos(+-inf)domainny-
cosh(+-large)overflowyye.g., cosh(DBL_MAX)
erf(+-small)underflownyFor subnormal x
erfc(x)underflownyResult underflows but produces representable (i.e., subnormal) result; e.g., erfc(27)
exp(+large)overflowyy-
exp(-large)underflowyy-
exp10(+large)overflowyyGNU extension, but inconsistent with exp()
exp10(-large)underflownyGNU extension, but inconsistent with exp()
exp2(+large)overflowyy-
exp2(-large)underflowyy-
expm1(+large)overflowny-
fabs()---No errors occur
fdim()overflownye.g., fdim(DBL_MAX, -DBL_MAX)
floor()---No errors occur
fma()domainnyVarious causes, e.g., one of x or y is an infinity and the other is 0.
fma()overflownye.g., fma(DBL_MAX, DBL_MAX, 0)
fma()underflownye.g., fma(DBL_MIN, DBL_MIN, 0)
fmax()---No errors occur
fmin()---No errors occur
fmod(+-inf,y)domainny-
fmod(x,0)domainyy-
hypot()overflowyye.g., hypot(DBL_MAX, DBL_MAX)
hypot()underflownye.g., if both arguments are small subnormal numbers
ilogb(+-inf)domainnnDoes correctly return INT_MAX
ilogb(0)domainnyDoes correctly return FP_ILOGB0
ilogb(nan)domainnyDoes correctly return FP_ILOGBNAN
j0()underflowyne.g., j0(DBL_MAX)
j1()underflowyn-
jn()underflowyn-
ldexp(x, +large-exp)overflowyy-
ldexp(x, -large-exp)underflowyy-
lgamma()overflowyye.g., lgamma(DBL_MAX)
lgamma()polenyOccurs when x is a non-positive integer; errno is set to EDOM (should be ERANGE)
llrint()domainnyx is NaN, infinity, or too large to store in a long long
llround()domainnyx is NaN, infinity, or too large to store in a long long
log(0)poleyy-
log(x<0)domainyy-
log10(0)poleyy-
log10(x<0)domainyy-
log1p(-1)poleny-
log1p(x<-1)domainny-
log2(0)poleyy-
log2(x<0)domainyy-
logb(0)poleny-
lrint()domainnyx is NaN, infinity, or too large to store in a long
lround()domainnyx is NaN, infinity, or too large to store in a long
nearbyint()---No errors occur
nextafter()overflownye.g., nextafter(DBL_MAX, +inf)
nextafter()underflownye.g., nextafter(DBL_MIN, 0);
nexttoward()overflownye.g., nexttoward(DBL_MAX, +inf)
nexttoward()underflownye.g., nexttoward(DBL_MIN, 0);
pow(0, -y)pole (0, neg)nyerrno is set to EDOM (should be ERANGE)
pow(x,y)overflowyy

Suitable values to cause overflow (e.g., pow(2, 1e100))

pow(x,y)underflowyy

Suitable values to cause underflow (e.g., pow(2, -1e100)

pow(x<0,>

domainyy-
remainder(+-inf,y)domainny-
remainder(x,0)domainyy-
remquo(+-inf,y)domainny-
remquo(x,0)domainny-
rint()---No errors occur
round()---No errors occur
scalb()overflownye.g., scalb(DBL_MAX, 200)
scalb()underflownye.g., scalb(DBL_MAX, -200)
scalb(0,+inf)domainny-
scalbln()overflownye.g., scalbln(DBL_MAX, 200)
scalbln()underflownye.g., scalbln(DBL_MAX, -200)
scalbn()overflownye.g., scalbn(DBL_MAX, 200)
scalbn()underflownye.g., scalbn(DBL_MAX, -200)
sin(+-inf)domainny-
sinh(+-large)overflowy-e.g., sinh(DBL_MAX)
sqrt(x<0)domainyy-
tan(+-inf)domainny-
tan(pi/2)overflow--No test possible, since the best approximation of pi/2 in double precision only yields a tan() value of 1.633e16.
tanh()---No errors occur
tgamma()underflownyOccurs for ranges of x values between negative integers, e.g., tgamma(-10000.5)
tgamma(+-0)poleyy-
tgamma(+large)overflowyy-
tgamma(-inf)domainny

Note the difference from
tgamma(x<0)

tgamma(x<0)domainyyFor finite x
trunc()---No errors occur
y0()overflow--Not possible to overflow with double
y0()underflowyne.g., y0(DBL_MAX)
y0(0)polennerrno is set to EDOM (should be ERANGE)
y0(x<0)domainyy-
y1()overflow--Not possible to overflow with double
y1()underflowyne.g., y1(DBL_MAX)
y1(0)polennerrno is set to EDOM (should be ERANGE)
y1(x<0)domainyy-
yn()overflownye.g., yn(1000, DBL_MIN)
yn()underflowyne.g., yn(10, DBL_MAX)
yn(0)polennerrno is set to EDOM (should be ERANGE)
yn(x<0)domainyy-

Tuesday, August 5, 2008

man-pages-3.06 is released

I've uploaded man-pages-3.06 into the release directory (or view the online pages). The main changes in man-pages-3.06 are updates to around 65 math pages. The changes are broadly as follows:
  • RETURN VALUE: in many cases, the math pages lacked descriptions of the return value when the function arguments are special cases such as +0, -0, NaN (not-a-number), +infinity, -infinity, etc. This has been fixed. I carried out tests on glibc 2.8 to ensure that the behavior of all of these functions matches the RETURN VALUE descriptions (and the POSIX.1-2001 requirements).
  • BUGS: a BUGS sections was added to a few pages to document deviations from from expected behavior.
  • ERRORS: many pages lacked a clear (or indeed any) description of how errno is set on error and what exception is raised for each error. This has been fixed. The ERRORS sections are now generally headed up as per the POSIX.1 way of doing things, describing Pole, Range, and Domain errors, as applicable. I carried out tests on glibc 2.8 to ensure that all of these functions match the ERRORS descriptions. Deviations from POSIX.1-2001 requirements have been filed as glibc bug reports, and noted in the man pages. (The pages now describe the situation for ERRORS as at glibc 2.8. I may eventually try and extend the text with descriptions of changes in older versions of glibc; watch for a post on that subject in the next days, when I'll describe my test scripts.)
  • Fixed feature test macros (FTMs). Often, the FTM requirements for the float and long double versions of a math function are different from the requirements for the double version. Each math page now shows the correct FTM requirements for all three versions of the function(s) it describes. This may have required either a change to the existing FTM text (if the requirements for the double function were already described), or the addition of an FTM description to a SYNOPSIS where one was not previously present (typically because the double version of the function does not require any FTMs to be defined). A few outright errors in the documentation of FTM requirements were also fixed.
  • CONFORMING TO: in many cases, POSIX.1-2001 was not mentioned. Where a function is specified in POSIX.1-2001, this is now noted. Also, statements about what other standards a function conforms to were generally clarified. (The wording about which functions conformed to C99 was previously often done as an add-on sentence; now it is made part of the first sentence of the CONFORMING TO section, along with mention of POSIX.1-2001.)
While making all of the above changes, I split out some logically separate material from existing pages to create three new pages: erfc(3) (formerly part of erf(3)), scalbln(3) (formerly part of scalb(3)), and y0(3) (formerly part of j0(3)).

P.S. One point that is still not covered in any page is the circumstances that generate inexact (FE_INEXACT) floating-point exceptions. (The details for these exceptions are not specified in POSIX.1-2001, and I haven't gone looking for the standards that describe the details.) If anyone has thoughts on what needs to be done about this, I'm open to ideas.

Grünegg

Monday, July 28, 2008

Handling bug reports explicitly

(Thoughts after a recent mail conversation...)

As a project maintainer, it's helpful to all concerned, but most especially you, if you are as explicit as possible when handling bug reports. In other words, explain to the reporter exactly what you did to fix the problem they reported, and why you did it. (This might be as simple as saying you applied a patch written by the reporter, or showing the patch you wrote and applied, but usually that's not enough.) If you did nothing, explain why. If the report was made by email to a list, then make sure you CC the list with your explanation. If the report was via a bug-reporting facility, then make sure that your explanation goes into the facility.

This helps you because:

  • It provides a historical record that will help you remember later what you did and why, and allow others to go back and find that information.
  • It encourages your good bug reporters to make future reports, because they can see that their reports make a difference.
  • It will help your bad bug reporters make better reports in the future.
  • Perhaps most importantly... You'll always make mistakes (misunderstand the report, make the wrong fix, etc.): if you let as many people as possible (the reporter, readers of the mailing list to which the bug report was written, followers of traffic on the bug reporting facility) know as precisely as possible what you did, then you give them all a chance to spot your mistake. I've had (far too) many of my mistakes spotted by others by explicitly explaining what I've done. (People could always find out what you did by hunting down diffs in a source repository, or a tarball, or whatever, but the effort involved means that many people won't do that.)
It helps others because:
  • The reporter of the bug knows how their problem got fixed.
  • If you are an upstream package maintainer, it will help downstream maintainers know what you did and why.
  • If you are a downstream package maintainer, it will help the upstream maintainer know what you did and why, which will help them decide whether to integrate your downstream changes upstream. 
(2012-03-25: Edit to fix a minor wording problem.)

Wednesday, July 23, 2008

man-pages-3.05 is released

I've uploaded man-pages-3.05 into the release directory (or view the online pages). Notable changes in man-pages-3.05 are:

  • A new math_error(7) page describes how to diagnose errors when calling math functions.
  • A new matherr(3) page describes the older SVID-specified mechanism for diagnosing errors from math functions.

There are a few other minor changes, but this week's release is indeed smaller than usual. That's because I'm working on pending changes to almost all of the math man pages to improve: the discussion of return values, treatment of special values (+0, -0, infinities, NaN), and error diagnostics; the accuracy of the description of feature test macro requirements; and various other details. Expect a (very) big release next week.

Blick auf die Grünegg, Konolfingen

Wednesday, July 16, 2008

Capabilities have fully arrived, finally

Linux 2.6.26 is out, which means that a complete Linux capabilities implementation has finally arrived, since we now have:

  • The ability to attach capability sets to files (added in 2.6.24), so that a process can acquire capabilities during an execve(2).
  • A CAP_SETPCAP capability with the proper semantics (since 2.6.25).
  • A per-thread capability bounding set (added in 2.6.25).
  • The per-thread securebits flags (added in 2.6.26), which can be used to restrict a thread and its children to a pure capabilities-only environment (i.e., one in which there is no special treatment of UID 0).
All of the details are provided in the recently revised capabilities(7) man page. A couple of other useful places to look for information on capabilities are Serge Hallyn's article, POSIX file capabilities: Parceling the power of root, and Chris Friedhoff's page on capabilities.

(2012-02-20: updated link to Serge Hallyn's article.)

man-pages-3.04 is released

I've uploaded man-pages-3.04 into the release directory (or view the online pages). Notable changes in man-pages-3.04 are:

  • A new utimensat(2) page describes the utimensat() system call (new in kernel 2.6.22, fixed in 2.6.26) and the futimens() library function.
  • A new end(3) page describes the end, etext, and edata variables.
  • The capget(2) page was updated by Andrew Morgan, in line with changes in Linux capabilities in kernels 2.6.24, 2.6.25, and 2.6.26.
  • The capabilities(7) page adds discussion of: file capabilities (new in Linux 2.6.24), the per-thread capability bounding set (new in Linux 2.6.25); the changed semantics for CAP_SETPCAP (Linux 2.6.25); per-thread securebits flags (new in Linux 2.6.26); three new capabilities, CAP_MAC_ADMIN, CAP_MAC_OVERRIDE, CAP_SETFCAP (Linux 2.6.25); historical CAP_SETPCAP semantics; the rules governing changes to capability sets; and the rationale for the inheritable set and capability bounding set. There were also many other more minor changes to this page. Thanks to Serge Hallyn for his contributions here.
  • The getrusage(2) page adds a description of the RUSAGE_THREAD option, which is new in Linux 2.6.26.
  • Many changes were made to the prctl(2) page, including adding documentation of: PR_CAPBSET_READ and PR_CAPBSET_DROP (thanks to Serge Hallyn); PR_GET_TSC and PR_SET_TSC (thanks to Erik Bosman); PR_SET_SECCOMP and PR_GET_SECCOMP; and PR_SET_SECUREBITS and PR_GET_SECUREBITS.
  • The proc(5) page adds documentation of /proc/config.gz (new in kernel 2.6); /proc/sys/vm/oom_kill_allocating_task (new in Linux 2.6.24); /proc/sys/vm/oom_dump_tasks (new in Linux 2.6.25); and /proc/sys/vm/panic_on_oom (new in Linux 2.6.18).
  • The getopt(3) page adds details on the use of optind for restarting an argument list scan.
  • The memchr(3) page adds a description of rawmemchr().
  • There were also very many minor content and and minor and major formatting fixes, many of them triggered by the around 70 mailed reports that I received from the Alain Portal, the French translator of man-pages. (Alain also provided dozens of reports that led to fixes in man-pages-3.03.) Thanks Alain!

Tuesday, July 8, 2008

man-pages-3.03 is released

I've uploaded man-pages-3.03 into the release directory (or view the online pages). Notable changes in man-pages-3.03 are:

  • A new cpuset(7) page, written by Paul Jackson, describes the cpuset file system, the mechanism introduced in Linux 2.6.12 for confining processes to designated processors and nodes. This enormous page becomes the fourth largest in man-pages. Thanks for a great contribution Paul!

  • A new getcpu(2) page, written by Andi Kleen, to document the getcpu(2) system call, introduced in Linux 2.6.19.

  • A new sched_getcpu(3) page documents a glibc wrapper for getcpu(2).

  • The readdir(3) page adds a description of readdir_r(3), the reentrant analog of readdir(3).

  • The signal(7) page adds a section on system call restarting (SA_RESTART), and describes the aberrant Linux behavior whereby a stop signal plus SIGCONT can interrupt some system calls, even if no signal handler has been established.

Wednesday, July 2, 2008

man-pages-3.02 is released

I've uploaded man-pages-3.02 into the release directory (or view the online pages).

Notable changes in man-pages-3.02 are:

  • A new clock_nanosleep(2) page, describing the system call introduced in kernel 2.6.

  • A rewritten getgrouplist(3) page, which provides additional information and an example program.

  • A new getutmp(3) page documenting the getutmp(3) and getutmpx(3) functions.

  • A new gnu_get_libc_version(3) page documenting get_gnu_libc_version(3) and get_gnu_libc_release(3).

  • A new sigwait(3) page documenting the sigwait(3) library function.

  • A new shm_overview(7) page providing an overview of the POSIX shared memory API.

  • Additional information in the sigreturn(2) man page.

  • Additions and updates to various pages describing the login accounting APIs, including: a new getutmp(3) page documenting the getutmp(3) and getutmpx(3) functions; and various improvements in the getutent(3) and utmp(5) pages.
2014-05-21: Some typos fixed

Monday, June 30, 2008

What's wrong with kernel-userland interface development?

So I came to write the man pages for utimensat() and futimens(), interfaces that are specified in the upcoming POSIX.1 revision, and recently added to Linux (kernel 2.6.22, glibc 2.6), and it's a familiar story: bugs, and yet more bugs.

This case is a little worse than usual (the gory details are below), but hardly exceptional: it was a similar story with other recent APIs, such as splice(), signalf(), and the timerfd API (the last of which was ultimately redesigned after some pushing by me). In each case, an interface that was already released in a stable kernel turned out to have easy-to-find bugs when I came to document and test it. All that was required was to come up with something like a reasonable written specification (i.e., a man page), and then start testing against that specification.

The following seems (to me) like a reasonable development model for new kernel userland APIs:

  1. Write a more or less complete design specification (preferably something like a man page, which I'm more than happy to edit and review at this stage in the process), and perhaps even code up an initial version of the interface.

  2. Get review comments about the design; revise the design if necessary.

  3. Implement something like a final version of the interface.

  4. Test the interface. Preferably: test in collaboration with other people who had nothing to do with the implementation (they will think about the interface in different ways from the implementer). If it's sensible to do so (and usually it is), produce a test suite to check the correctness of as many operational cases of the interface as possible (and then send that suite to the Linux Test Project).

  5. Write the interface documentation (i.e., a man page) for userland programmers, and send it to me for editing and review. (If you do this smart, then you can just recycle the work from the first step.)

  6. Get the interface accepted into mainline.
However, as far as I can see, the actual development model for kernel-userland interfaces seems to work something like this:
  1. Code up the new interface.

  2. Post the code to LKML, explaining that "I tested it, and it works", and, maybe, include some brief documentation of the interface.

  3. Often, someone looks at the code and suggests some fixes. Usually, no one else does any testing.

  4. Code gets accepted into mainline.

  5. Usually, some few dot releases later: someone (often me) who had nothing to do with the earlier steps writes the man page.

  6. Often, some few dot releases later: find and fix all of the bugs (e.g., in the case of utimensat(), released for 2.6.22, the fixes will only make it into 2.6.26 at the earliest).

  7. Occasionally, some few dot releases later: realize that the interface could have been better designed, but it's too late now to change it, because we can't break the ABI.
Most kernel-userland interfaces are seeing far too little testing before release. This hurts because:
  • Userland programmers end up dealing with kernel bugs they shouldn't have to deal with. (When this happens often, it can damage the reputation of the Linux kernel-userland interface, and userland programmers may become wary of adopting new interfaces, which can further delay any kind of real-world testing and adoption of the interface.)

  • Code that uses new interfaces may have to special case for ABI bugs that were present in the first few kernel releases that contained the bug.
Similarly, many kernel-userland interfaces could do with more design review as well. This can hurt even more than interfaces released with bugs, since, once an interface is released, it is at least difficult, but often impossible to modify the design, since doing so would break the kernel-userland ABI. (With timerfd, we got lucky: the interface was in any case broken by a bug, which gave a more or less blank slate for a redesign.) As a result, userland programmers usually just have to live with the bad design.

I'll have more time for testing and review nowadays, and hopefully I'll catch more problems before they are released into stable kernels, but there's certainly more than one person can do. And, as described above, there is a culture problem when it comes to adding kernel-userland interfaces on Linux. The question is whether the culture can be changed. (And here its worth repeating a point that I often make: one could perhaps try and blame individual developers for buggy interfaces, but that doesn't really get to the core issue (everyone is going to write bugs): the real problem is the process by which kernel-userland interface changes are accepted into the kernel.)

The Gory Details
For those who are interested, here are the problems I found with utimensat() and futimens(), explained by annotating the current draft of the man page.

SYNOPSIS
    #include <sys/stat.h>
 
    int utimensat(int dirfd, const char *pathname,
                  const struct timespec times[2], int flags);
 
    int futimens(int fd, const struct timespec times[2]);
 
DESCRIPTION
    utimensat() and futimens() update  the  timestamps  of  a
    file  with nanosecond precision.  This contrasts with the
    historical utime(2) and utimes(2), which permit only sec-
    ond and microsecond precision, respectively, when setting
    file timestamps.

This is the first of the advantages of the new interfaces: greater precision for setting timestamps. (The next revision of POSIX.1 specifies nanosecond file timestamps, and extends stat(2) to allow their retrieval. File system support is needed, and can't necessarily be retrofitted to some older file systems. Currently, Linux supports nanosecond timestamps on XFS, JFS, and ext4.)

    With utimensat() the file is specified via  the  pathname
    given in pathname.  With futimens() the file whose times-
    tamps are to be updated is specified  via  an  open  file
    descriptor, fd.
 
    For  both calls, the new file timestamps are specified in
    the array times: times[0] specifies the new "last  access
    time" (atime); times[1] specifies the new "last modifica-
    tion time" (mtime).  Each of the elements of times speci-
    fies  a  time  in seconds and nanoseconds since the Epoch
    (00:00:00, 1 Jan 1970, UTC), in a structure of  the  fol-
    lowing form:
 
        struct timespec {
            time_t tv_sec;        /* seconds */
            long   tv_nsec;       /* nanoseconds */
        };
 
    If  the  tv_nsec  field of one of the timespec structures
    has the special value UTIME_NOW, then  the  corresponding
    file  timestamp  is  set  to  the  current  time.  If the
    tv_nsec field of one of the timespec structures  has  the
    special  value  UTIME_OMIT,  then  the corresponding file
    timestamp is left unchanged.  In both of these cases, the
    value of the corresponding tv_sec field is ignored.

These are the other advantages of the new interfaces.

With utime(2) and utimes(2), to change just one of the timestamps, we must make a call to stat(2) to retrieve the current timestamps, use one of the timestamps to initialize the times element that we don't want to change, and then call utimensat() with the desired value for the other timestamp. This can be subject to race conditions: if another process updates the file timestamps between the two calls, then that update will be lost. UTIME_OMIT allows us to avoid this problem.

UTIME_NOW exists mainly as a convenience: it allows us to avoid fetching the current time (using gettimeofday(2) or similar) in order to set a file timestamp to "now".

    If  times  is  NULL,  then both timestamps are set to the
    current time.
 
Permissions requirements
    To set both file timestamps to the  current  time  (i.e.,
    times is NULL, or both tv_nsec fields specify UTIME_NOW),
    either:
 
    1. the caller must have write access to the file;
 
    2. the caller's effective user ID must match the owner of
       the file; or
 
    3. the caller must have appropriate privileges.
 
    To  make any change other than setting both timestamps to
    the current time (i.e.,  times  is  not  NULL,  and  both
    tv_nsec  fields are not UTIME_NOW and both tv_nsec fields
    are not UTIME_OMIT), either condition 2 or 3  above  must
    apply.
 
    If  both tv_nsec fields are specified as UTIME_OMIT, then
    no file ownership or permission checks are performed, and
    the  file  timestamps  are  not modified, but other error
    conditions may still be detected.
 
utimensat() specifics
    [Details of dirfd and flags arguments omitted.]

For details of the dirfd argument, see openat(2).

RETURN VALUE
    On success, utimensat()  and  futimens()  return  0.   On
    error,  -1  is  returned and errno is set to indicate the
    error.
 
ERRORS
    [Various other errors that are irrelevant  to  this  post
    have been omitted.]
 
    EACCES times   is   NULL,  or  both  tv_nsec  values  are
           UTIME_NOW, but the effective effective ID  of  the
           caller  does  not match the owner of the file, the
           caller does not have write access to the file, and
           the caller is not privileged (Linux: does not have
           either  the  CAP_FOWNER  or  the  CAP_DAC_OVERRIDE
           capability).
 
    EACCES times   is   NULL,  or  both  tv_nsec  values  are
           UTIME_NOW, and the file is marked immutable.
 
    EPERM  The caller attempted to change one or both  times-
           tamps  to  a value other than the current time, or
           to change one of the  timestamps  to  the  current
           time  while leaving the other timestamp unchanged,
           (i.e., times is not NULL, both tv_nsec fields  are
           not  UTIME_NOW,  and  both  tv_nsec fields are not
           UTIME_OMIT) but the  caller's  effective  user  ID
           does  not  match the owner of file, and the caller
           is  not  privileged  (Linux:  does  not  have  the
           CAP_FOWNER capability).
 
    EPERM  times  is not NULL, and the file is marked append-
           only or immutable.
 
NOTES
    On Linux, futimens() is a library function implemented on
    top of the utimensat() system call.  To support this, the
    Linux utimensat() system call implements  a  non-standard
    feature:  if pathname is NULL, then the system call modi-
    fies the timestamps of the open file referred to  by  the
    file  descriptor  dirfd  (which  may refer to any type of
    file).  Using this feature, the call  futimens(fd, times)
    is implemented as:
 
        utimensat(fd, NULL, times, 0);
 
BUGS
    Several  bugs  afflict utimensat() and futimens() on ker-
    nels before 2.6.??.  

Really, there should have been a test suite to go along with the initial implementation. There wasn't, unfortunately, so I wrote one (later revised a little before it went to LTP) whose results can be seen here.

I've posted patches to fix all of the bugs, and Andrew Morton has accepted them into -mm, and from there, they seem to have gone upstream to Al Viro. It's just a question of when they will get pushed into mainline. It'd be nice if they make it into 2.6.26, but maybe they won't, since it is already getting very late in the -rc cycle. (Update, 6 Jul 08: the patches have gone into -rc9.)

                         These bugs  are  either  non-confor-
    mances  with the POSIX.1 draft specification or inconsis-
    tencies with historical Linux behavior.
 
    * POSIX.1 specifies that if one of the tv_nsec fields has
      the  value  UTIME_NOW  or UTIME_OMIT, then the value of
      the  corresponding  tv_sec  field  should  be  ignored.
      Instead,  the  value of the tv_sec field is required to
      be 0 (or the error EINVAL results).   

This was a simple and obvious divergence from the specification.

    * Various bugs mean that for the purposes  of  permission
      checking, the case where both tv_nsec fields are set to
      UTIME_NOW isn't always treated the same  as  specifying
      times  as NULL, and the case where one tv_nsec value is
      UTIME_NOW and the other is UTIME_OMIT isn't treated the
      same  as  specifying  times as a pointer to a structure
      containing arbitrary time values.  As a result, in some
      cases:  a)  file timestamps can be updated by a process
      that shouldn't have permission to perform  updates;  b)
      file  timestamps  can't  be  updated  by a process that
      should have permission to perform updates; and  c)  the
      wrong errno value is returned in case of an error.   

There are multiple error cases here:
  • If one of the tv_nsec fields is UTIME_OMIT and the other is UTIME_NOW, then the error EPERM should occur if the process's effective user ID does not match the file owner and the process is not privileged. Instead, the call successfully changes one of the timestamps.
  • If the file is not writable by the effective user ID of the process and the process's effective user ID does not match the file owner and the process is not privileged, and times is NULL, then the error EACCES results. This error should also occur if times points to a structure in which both tv_nsec fields are UTIME_NOW. Instead the call succeeds.
  • If a file is marked as append-only (see chattr(1)), then Linux traditionally (i.e., utime(2), utimes(2)), permits a NULL times argument to be used in order to update both timestamps to the current time. For consistency, utimensat() and futimens() should also produce the same result when given a times argument that points to a structure in which both tv_nsec fields are UTIME_NOW. Instead, the call fails with the error EPERM.
  • If a file is marked as immutable (see chattr(1)), then Linux traditionally (i.e., utime(2), utimes(2)), gives an EACCES error if times is NULL. For consistency, utimensat() and futimens() should also produce the same result when given a times that points to a structure in which both tv_nsec fields are UTIME_NOW. Instead, the call fails with the error EPERM.
    * POSIX.1  says  that  a process that has write access to
      the file can make a call with times as  NULL,  or  with
      times  pointing  to  a  structure in which both tv_nsec
      fields are UTIME_NOW,  in  order  to  update  the  both
      timestamps  to  the  current time.  However, futimens()
      instead checks whether the  access  mode  of  the  file
      descriptor  allows  writing.

This means that a process with a file descriptor that allows writing could change the timestamps of a file for which it does not have write permission; conversely, a process with a read-only file descriptor won't be able to update the timestamps of a file, even if it has write permission on the file.

Updated, 2012-03-06: Changed link to tarball containing tests

Wednesday, June 25, 2008

man-pages-3.01 is released

I've uploaded man-pages-3.01 into the release directory (or view the online pages).

Notable changes in man-pages-3.01 are:

  • A new hostname(7) page (based on the FreeBSD page) describing hostname resolution.
  • A new symlink(7) page (heavily modified from the original FreeBSD page) describing symbolic links.
  • A rewritten acct(5) page providing much more detail on the information written to the process accounting file.
  • The getrlimit(2) page adds a description of RLIMIT_RTTIME limit, new in Linux 2.6.25.
  • The mkstemp(3) page adds a description of mkostemp(), new in glibc 2.7.
  • The core(5) page adds a description (and an example program for) of the core_pattern pipe syntax, which appeared in Linux 2.6.19, and documents /proc/PID/coredump_filter, new in kernel 2.6.23.
  • The proc(5) page adds details for a number of previously undocumented /proc interfaces, including /proc/PID/oom_score, /proc/PID/oom_adj, /proc/PID/limits, /proc/PID/fdinfo/*, /proc/PID/mountinfo, /proc/PID/mountstats, and /proc/PID/status.
  • The time(7) page enhances the discussion of jiffies, and adds a section on high-resolution timers.
  • The unix(7) page adds a clear description of the three types of address that can appear in the sockaddr_un structure: pathname, unnamed, and abstract.
  • Many other pages saw significant changes, including brk(2), chmod(2), chown(2), nanosleep(2), open(2), sched_setscheduler(2), syscalls(2), ftime(3), getaddrinfo(3), inet(3), inet_pton(3), scanf(3), strerror(3), random(4), and locale(5).

Tuesday, June 17, 2008

man-pages-3.00 is released

I've uploaded man-pages-3.00 into the release directory (or view the online pages). Notable changes in man-pages-3.00 are:

  • The POSIX man pages (man pages sections 0p, 1p, 3p) are now split out into a separate package, which can be downloaded here. This makes sense because the POSIX pages are logically separate: they are copies of specifications from the POSIX.1 standard (to be precise, we are currently redistributing pages from the 2003 Technical Corrigendum 1), that we have kindly been given permission to publish by the IEEE and The Open Group. Furthermore, the POSIX pages are only updated rarely (e.g., occasional formatting fixes, and periodic updates when a new version of the standard is released, such as the revision due to appear later this year), so there is no need to redistribute them with each release of man-pages.

  • After Stuart Brady noted that the quotes around single-quoted characters were not formatted well in UTF-8 output, I've globally replaced the characters used in the source files for quoting in man-pages in order to try and produce better formatted output for both ASCII and UTF-8 xterms, and also to bring greater consistency in general to the use of single and double quotes in the pages. This may not be the final word on the subject, but at least things should be somewhat improved.