[go-nuts] Proposal: expand the "Commonly known Unix errors" to match SUSv3

109 views
Skip to first unread message

Giles Lean

unread,
Apr 30, 2010, 9:27:30 PM4/30/10
to golan...@googlegroups.com

Hi,

Is there a reason not to expand the "Commonly known Unix
errors" in the os package to the set of errors defined in the
"Single Unix Standard, version 3" aka SUSv3 aka IEEE Std
1003.1-2001?

With the exception of the optional XSI STREAMS errors (which I
would propose to leave out) all the Unix-like platforms
(Linux, OS X, *BSD) [but not Windows, I imagine] already
define these error values in their syscall packages.

By putting these errors into the os package it reduces the
need to delve into the syscall package, which as has been
discussed before is intended _not_ to be needed by ordinary
applications.

Unless I hear objections, I'll plan to submit a CL in a few
days. (It's a trivial change -- see below -- but if there's
to be debate, better that it be here than golang-dev.)

Regards,

Giles

diff -r 3bd92eac2187 src/pkg/os/error.go
--- a/src/pkg/os/error.go Wed Apr 28 14:06:25 2010 +1000
+++ b/src/pkg/os/error.go Wed Apr 28 15:15:37 2010 +1000
@@ -40,45 +40,86 @@
return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK)
}

-// Commonly known Unix errors.
+// Errors defied by POSIX (from IEE Std 1003.2001), without ENODATA,
+// ENOSR, ENOSTR, or ETIME which are part of the XSI STREAMS option.
var (
- EPERM Error = Errno(syscall.EPERM)
- ENOENT Error = Errno(syscall.ENOENT)
- ESRCH Error = Errno(syscall.ESRCH)
- EINTR Error = Errno(syscall.EINTR)
- EIO Error = Errno(syscall.EIO)
- ENXIO Error = Errno(syscall.ENXIO)
- E2BIG Error = Errno(syscall.E2BIG)
- ENOEXEC Error = Errno(syscall.ENOEXEC)
- EBADF Error = Errno(syscall.EBADF)
- ECHILD Error = Errno(syscall.ECHILD)
- EDEADLK Error = Errno(syscall.EDEADLK)
- ENOMEM Error = Errno(syscall.ENOMEM)
- EACCES Error = Errno(syscall.EACCES)
- EFAULT Error = Errno(syscall.EFAULT)
- EBUSY Error = Errno(syscall.EBUSY)
- EEXIST Error = Errno(syscall.EEXIST)
- EXDEV Error = Errno(syscall.EXDEV)
- ENODEV Error = Errno(syscall.ENODEV)
- ENOTDIR Error = Errno(syscall.ENOTDIR)
- EISDIR Error = Errno(syscall.EISDIR)
- EINVAL Error = Errno(syscall.EINVAL)
- ENFILE Error = Errno(syscall.ENFILE)
- EMFILE Error = Errno(syscall.EMFILE)
- ENOTTY Error = Errno(syscall.ENOTTY)
- EFBIG Error = Errno(syscall.EFBIG)
- ENOSPC Error = Errno(syscall.ENOSPC)
- ESPIPE Error = Errno(syscall.ESPIPE)
- EROFS Error = Errno(syscall.EROFS)
- EMLINK Error = Errno(syscall.EMLINK)
- EPIPE Error = Errno(syscall.EPIPE)
- EAGAIN Error = Errno(syscall.EAGAIN)
- EDOM Error = Errno(syscall.EDOM)
- ERANGE Error = Errno(syscall.ERANGE)
- EADDRINUSE Error = Errno(syscall.EADDRINUSE)
- ECONNREFUSED Error = Errno(syscall.ECONNREFUSED)
- ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG)
- EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT)
+ E2BIG Error = Errno(syscall.E2BIG)
+ EACCES Error = Errno(syscall.EACCES)
+ EADDRINUSE Error = Errno(syscall.EADDRINUSE)
+ EADDRNOTAVAIL Error = Errno(syscall.EADDRNOTAVAIL)
+ EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT)
+ EAGAIN Error = Errno(syscall.EAGAIN)
+ EALREADY Error = Errno(syscall.EALREADY)
+ EBADF Error = Errno(syscall.EBADF)
+ EBADMSG Error = Errno(syscall.EBADMSG)
+ EBUSY Error = Errno(syscall.EBUSY)
+ ECANCELED Error = Errno(syscall.ECANCELED)
+ ECHILD Error = Errno(syscall.ECHILD)
+ ECONNABORTED Error = Errno(syscall.ECONNABORTED)
+ ECONNREFUSED Error = Errno(syscall.ECONNREFUSED)
+ ECONNRESET Error = Errno(syscall.ECONNRESET)
+ EDEADLK Error = Errno(syscall.EDEADLK)
+ EDESTADDRREQ Error = Errno(syscall.EDESTADDRREQ)
+ EDOM Error = Errno(syscall.EDOM)
+ EDQUOT Error = Errno(syscall.EDQUOT)
+ EEXIST Error = Errno(syscall.EEXIST)
+ EFAULT Error = Errno(syscall.EFAULT)
+ EFBIG Error = Errno(syscall.EFBIG)
+ EHOSTUNREACH Error = Errno(syscall.EHOSTUNREACH)
+ EIDRM Error = Errno(syscall.EIDRM)
+ EILSEQ Error = Errno(syscall.EILSEQ)
+ EINPROGRESS Error = Errno(syscall.EINPROGRESS)
+ EINTR Error = Errno(syscall.EINTR)
+ EINVAL Error = Errno(syscall.EINVAL)
+ EIO Error = Errno(syscall.EIO)
+ EISCONN Error = Errno(syscall.EISCONN)
+ EISDIR Error = Errno(syscall.EISDIR)
+ ELOOP Error = Errno(syscall.ELOOP)
+ EMFILE Error = Errno(syscall.EMFILE)
+ EMLINK Error = Errno(syscall.EMLINK)
+ EMSGSIZE Error = Errno(syscall.EMSGSIZE)
+ EMULTIHOP Error = Errno(syscall.EMULTIHOP)
+ ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG)
+ ENETDOWN Error = Errno(syscall.ENETDOWN)
+ ENETRESET Error = Errno(syscall.ENETRESET)
+ ENETUNREACH Error = Errno(syscall.ENETUNREACH)
+ ENFILE Error = Errno(syscall.ENFILE)
+ ENOBUFS Error = Errno(syscall.ENOBUFS)
+ ENODEV Error = Errno(syscall.ENODEV)
+ ENOENT Error = Errno(syscall.ENOENT)
+ ENOEXEC Error = Errno(syscall.ENOEXEC)
+ ENOLINK Error = Errno(syscall.ENOLINK)
+ ENOMEM Error = Errno(syscall.ENOMEM)
+ ENOPROTOOPT Error = Errno(syscall.ENOPROTOOPT)
+ ENOSPC Error = Errno(syscall.ENOSPC)
+ ENOSYS Error = Errno(syscall.ENOSYS)
+ ENOTCONN Error = Errno(syscall.ENOTCONN)
+ ENOTDIR Error = Errno(syscall.ENOTDIR)
+ ENOTEMPTY Error = Errno(syscall.ENOTEMPTY)
+ ENOTSOCK Error = Errno(syscall.ENOTSOCK)
+ ENOTSUP Error = Errno(syscall.ENOTSUP)
+ ENOTTY Error = Errno(syscall.ENOTTY)
+ ENXIO Error = Errno(syscall.ENXIO)
+ EOPNOTSUPP Error = Errno(syscall.EOPNOTSUPP)
+ EOVERFLOW Error = Errno(syscall.EOVERFLOW)
+ EPERM Error = Errno(syscall.EPERM)
+ EPIPE Error = Errno(syscall.EPIPE)
+ EPROTO Error = Errno(syscall.EPROTO)
+ EPROTONOSUPPORT Error = Errno(syscall.EPROTONOSUPPORT)
+ EPROTOTYPE Error = Errno(syscall.EPROTOTYPE)
+ ERANGE Error = Errno(syscall.ERANGE)
+ EROFS Error = Errno(syscall.EROFS)
+ ESPIPE Error = Errno(syscall.ESPIPE)
+ ESRCH Error = Errno(syscall.ESRCH)
+ ESTALE Error = Errno(syscall.ESTALE)
+ ETIMEDOUT Error = Errno(syscall.ETIMEDOUT)
+ ETXTBSY Error = Errno(syscall.ETXTBSY)
+ EWOULDBLOCK Error = Errno(syscall.EWOULDBLOCK)
+ EXDEV Error = Errno(syscall.EXDEV)
+ //ENODATA Error = Errno(syscall.ENODATA) // XSI STREAMS: No message is available on the STREAM head read queue.
+ //ENOSR Error = Errno(syscall.ENOSR) // XSI STREAMS: No STREAM resources.
+ //ENOSTR Error = Errno(syscall.ENOSTR) // XSI STREAMS: Not a STREAM.
+ //ETIME Error = Errno(syscall.ETIME) // XSI STREAMS: Stream ioctl() timeout.
)

// PathError records an error and the operation and file path that caused it.

hotei

unread,
Apr 30, 2010, 10:07:00 PM4/30/10
to golang-nuts
Giles,
I'll second your motion, since I just recently ran into some issues
with lack of appropriate predefined Error values to return. In
addition, I'd prefer to include the XSI values. Rather than make up
my own error codes I had planned to return a ENODATA value to indicate
an error - only to find out that it wasn't defined in the library.

For what it's worth I'd also like to see a comment by the error the
way you did in the XSI portion of your example as in ENOSTR... // "Not
a Stream". Rather than just bubbling up errors from the syscall layer
I was intending to use the ability to translate ENOSTR back into "Not
a Stream" on output. In other words, I need to see the choices of
error strings to pick the right E* code. Not a big deal, but it would
help keep doc in one spot.

if failure {
err := os.ENOSTR
fmt.Printf("err: %s\n", err.String() )
return err
}

Giles Lean

unread,
May 1, 2010, 1:07:10 AM5/1/10
to hotei, golang-nuts

hotei <ravens...@cox.net> wrote:

> In addition, I'd prefer to include the XSI values.

I have two reasons not to:

1. None of the current Go platforms implement XSI STREAMS, so
having the errors defined is of very little use

2. Only OS X defines errno values for the errors; they're not
present on Linux or FreeBSD, and any numbers we chose to
make up might collide with real errno values in future

> For what it's worth I'd also like to see a comment by the
> error the way you did in the XSI portion of your example as
> in ENOSTR... // "Not a Stream".

I merely put those comments there to indicate what the XSI
STREAMS names are, and to make it obvious that they're omitted
on purpose.

> Rather than just bubbling up errors from the syscall layer I
> was intending to use the ability to translate ENOSTR back
> into "Not a Stream" on output.

You already can, if I understand what you want: calling
String() on an os.Errno value produces the relevant text:

...
var err os.Errno = 2
fmt.Fprintf(os.Stderr, "errno %d: %s\n", err, err.String())
...

Produces:

errno 2: no such file or directory

(Of course, anyone who wants localised error messages is still
out of luck, but that's a different issue.)

Regards,

Giles



hotei

unread,
May 1, 2010, 1:36:33 AM5/1/10
to golang-nuts
My point is that programs other than syscalls might want to return
os.Error values. Since OS.X is based on FreeBSD I don't think
there's much chance of a name-space conflict but Apple does do some
strange things now and then.

The comment thing is a real problem for some of us. I remember what
EINVAL is, but right off the top I have no idea what EROFS stands
for. I'd have to look it up in a book. At least if it's in a comment
I can browse the source for the os.Errors and tell at a glance.
Believe it or not you can't grep the go source tree for EINVAL and get
it matched to the "invalid argument" string. The strings are
apparently stored in an array where they just happen to be in the
right place. Like I said, not a big deal, but a nice convenience.


On May 1, 1:07 am, Giles Lean <giles.l...@pobox.com> wrote:

Giles Lean

unread,
May 1, 2010, 2:16:40 AM5/1/10
to hotei, golang-nuts

hotei <ravens...@cox.net> wrote:

> My point is that programs other than syscalls might want to return
> os.Error values. Since OS.X is based on FreeBSD I don't think
> there's much chance of a name-space conflict but Apple does do some
> strange things now and then.

At the risk of straying off topic, _parts_ of OS X are based
on FreeBSD, and Apple have been quite willing to generate
conflicts between open source software and the versions of
same that they ship: look at rsync's -E option, as one I
remember off the top of my head.

I have no idea why Apple added the errno values for XSI
STREAMS, unless someone in marketing thought they might want
Unix branding one day.

Back on topic:

> The comment thing is a real problem for some of us. I
> remember what EINVAL is, but right off the top I have no
> idea what EROFS stands for.

OK, now I understand. Sadly I remember most of the errno
names and a goodly percentage of their values.

For a quick solution that'll work today (outside of Go) grep
whatever C include file contains those definitions on your
platform(s), for example on OS X:

$ grep EROFS /usr/include/sys/errno.h
#define EROFS 30 /* Read-only file system */

Or even use your favourite search engine. :-)

For Go, I suppose godefs could be updated to include the
comment in the generated syscall/zerrors_$GOOS_$ARCH.go files,
but I doubt that it's worth it on the Unix-like platforms when
you'd still end up using grep anyway ... you might as well
jump straight to the C include files.

Regards,

Giles

hotei

unread,
May 1, 2010, 9:04:40 AM5/1/10
to golang-nuts
Giles,
No problem. I understand the KIS principle. I'll just write a 'cheat
sheet' program that prints
all the err msg names, numbers and strings and keep that handy. I
work mostly in "userland"
(modeling and simulation stuff) so the syscalls are foreign territory
for me and I still need a map.

Still in support of your original proposal.

Hotei

Russ Cox

unread,
May 1, 2010, 4:09:21 PM5/1/10
to Giles Lean, golan...@googlegroups.com
I'm a bit skeptical that this is the right direction to go in.

This would only help programs that want to look for specific errors,
and t's almost never correct to be checking for specific Unix errors.
Go doesn't only run on Unix, and there is no guarantee that
a given routine will use the errno form of the error anyway
(for example, the routines in strconv return more descriptive
errors than just EINVAL).

Russ

Giles Lean

unread,
May 1, 2010, 5:48:15 PM5/1/10
to r...@golang.org, golan...@googlegroups.com

Russ Cox <r...@golang.org> wrote:

> I'm a bit skeptical that this is the right direction to go in.
>
> This would only help programs that want to look for specific errors,
> and t's almost never correct to be checking for specific Unix errors.

True, except when it is, and when it is, it's silly to have to
be using some errors from the os package and some from the
syscall package.

> Go doesn't only run on Unix, and there is no guarantee that
> a given routine will use the errno form of the error anyway
> (for example, the routines in strconv return more descriptive
> errors than just EINVAL).

If you want to be rigorous about it, perhaps the "Common Unix
Errors" in the "os" package should be reduced to those errors
ANSI C defines (I think there are ~three!), and the rest left
in "syscall"? (Why are they in "os" at all if they're not
supposed to be used?)

I know POSIX is rather overgrown and bloated (to put it
politely) and that those of you who worked/work on Plan9 must
find that especially irritating, however for many of us POSIX
is "the" operating system we use (in one flavour or another)
and to be successful Go has to play nice with it.

The errors we have now in the "os" package are neither fish
nor fowl, IMHO.

Go has the potential to displace C for a lot of uses, but it's
still only potential; a lot more rough edges need to be
knocked off.

You're welcome to hold a different opinion, of course.

I had thought this was a _minor_ rough edge. :sigh:

Giles

Russ Cox

unread,
May 1, 2010, 6:38:45 PM5/1/10
to Giles Lean, golan...@googlegroups.com
> I know POSIX is rather overgrown and bloated (to put it
> politely) and that those of you who worked/work on Plan9 must
> find that especially irritating, however for many of us POSIX
> is "the" operating system we use (in one flavour or another)
> and to be successful Go has to play nice with it.

Actually I was thinking about Windows.

Anyway it's very hard to tell from your diff how many errors
you are adding. Can you list just the new ones?

Russ

Giles Lean

unread,
May 1, 2010, 7:33:32 PM5/1/10
to r...@golang.org, golan...@googlegroups.com

Russ Cox <r...@golang.org> wrote:

> Actually I was thinking about Windows.

When I first looked at Go I was surprised at how Unix-biased
it was; I expect the current errors aren't a good match for it
already.

> Anyway it's very hard to tell from your diff how many errors
> you are adding. Can you list just the new ones?

Yeah, gofmt tends to expand diffs.

The current tip's os package has 37 "common" errors.

The change summary is 36 additional errors plus the four
commented out XSI STREAMS errors, and would result in a fairly
complete rather than "common" set:

EADDRNOTAVAIL
EALREADY
EBADMSG
ECANCELED
ECONNABORTED
ECONNRESET
EDESTADDRREQ
EDQUOT
EHOSTUNREACH
EIDRM
EILSEQ
EINPROGRESS
EISCONN
ELOOP
EMSGSIZE
EMULTIHOP
ENETDOWN
ENETRESET
ENETUNREACH
ENOBUFS
ENOLINK
ENOPROTOOPT
ENOSYS
ENOTCONN
ENOTEMPTY
ENOTSOCK
ENOTSUP
EOPNOTSUPP
EOVERFLOW
EPROTO
EPROTONOSUPPORT
EPROTOTYPE
ESTALE
ETIMEDOUT
ETXTBSY
EWOULDBLOCK
//ENODATA
//ENOSR
//ENOSTR
//ETIME

Regards,

Giles

Russ Cox

unread,
May 1, 2010, 7:53:37 PM5/1/10
to Giles Lean, golan...@googlegroups.com
>> Actually I was thinking about Windows.
>
> When I first looked at Go I was surprised at how Unix-biased
> it was; I expect the current errors aren't a good match for it
> already.

The current os.Error interface is a nice match for any system.
os.Errno is definitely Unix-specific, but there's an important
difference between the two. Routines in Go return os.Error;
very few of those happen to have concrete type os.Errno.
I bet at least some of these (and at least some of os has already)
can never happen in Go programs. For example, since Go provides
an abstracted view of the socket interface, it's not possible to see
(and thus never correct to test for) EISCONN, ENOTCONN, ENOTSOCK,
EINPROGRESS, EALREADY. For a different reason, you should never
see ENOSYS. And so on. Why expose them in package os?
If anything we should probably double-check what's in package os
and possibly prune some (AFNOSUPPORT, for example).

Is there a specific error missing that you need? I'd be happy to add it.
But I would prefer not to just throw in the kitchen sink, especially since
it gives a misleading impression about how Go reports errors.

Russ

Giles Lean

unread,
May 2, 2010, 4:35:53 AM5/2/10
to r...@golang.org, golan...@googlegroups.com

Russ Cox <r...@golang.org> wrote:

> The current os.Error interface is a nice match for any system.
> os.Errno is definitely Unix-specific, but there's an important
> difference between the two. Routines in Go return os.Error;
> very few of those happen to have concrete type os.Errno.
>
> ...
>
> Is there a specific error missing that you need?

Thanks, but don't worry about it. EILSEQ was appropriate to
some of my code, but I can create a suitable os.Error and use
that instead. If I'm ever implmenting a CL which really calls
for a new os.Errno value, I'll include it in the CL.

I do find the presence of incomplete functionality in Go
strange sometimes; I shall start to take partially implemented
features as a hint that I shouldn't be using the feature at
all. Perhaps that'll keep me more "in tune" with the design,
which I obviously still don't fully grok.

Giles

Russ Cox

unread,
May 2, 2010, 5:02:18 AM5/2/10
to Giles Lean, golan...@googlegroups.com
> Thanks, but don't worry about it. EILSEQ was appropriate to
> some of my code,

I'm curious: what was the context? Was the operating system
sending back EILSEQ or was your code generating it?
The typical behavior in Go is that if invalid UTF-8 must be
transformed or traversed, the invalid sequences turn into
the Unicode replacement character rather than cause an
error that aborts the operation. That aside, one of the reasons
that the os.Errno type is so infrequently used is that it contains
hardly any information about the error. Instead of just saying
"invalid byte sequence", it would be much more helpful to find
out what the invalid byte sequence was. Typically that
would be implemented as a separate error type (so you can
check for that kind of error) with enough data to print a good
error in the String method. For example, try passing an invalid
string like "1x" to strconv.Atoi and see what kind of errors
come back. It's more than just EINVAL.

Russ

Giles Lean

unread,
May 2, 2010, 5:41:09 AM5/2/10
to r...@golang.org, golan...@googlegroups.com

Russ Cox <r...@golang.org> wrote:

> I'm curious: what was the context? Was the operating system
> sending back EILSEQ or was your code generating it?

My code was generating it. On the basis of your explanation,
it probably shouldn't have been; certainly it didn't need to.

So long as the function and its callers agree on what errors
are what and either the caller or the callee has enough
context to generate a good quality error message (in this case
it is the caller who has that information) it's all cool.

Even having read "Effective Go" more than once and looked at a
lot of the standard packages I'm still working out what are
good ideas and what are bad ideas in idiomatic Go.

I must say I prefer to figure out which of my ideas are bad on
my own, but thanks for your explanations today.

Cheers,

Giles

P.S. I suppose too I am unused to the speed at which the Go
team work and your willingness to make changes when a need is
demonstrated.

I've worked with some groups where getting changes made was
hard (actually, very hard) and so getting things 100% covered
first time through was important, and taking a risk on adding
"just enough" of a feature was unwise merely from the overhead
if an issue had to be revisited. *Not* the way I'd advocate
any software group work, but I guess I'm still in
recovery. :-)

Giles Lean

unread,
May 2, 2010, 5:51:16 AM5/2/10
to r...@golang.org, golan...@googlegroups.com

I wrote to Russ:

> I must say I prefer to figure out which of my ideas are bad on
> my own, but thanks for your explanations today.

By which I mean I prefer to work such things out before I open
my big mouth on the mailing list, *not* that I don't
appreciate your responses!

(I hope that was clear anyway, but it's easy to misinterpret
email, so I'd prefer to clarify and be sure.)

Best regards,

Giles
Reply all
Reply to author
Forward
0 new messages