[comp.unix.questions] errno

allyn@sleepy.UUCP (Mark Allyn) (06/12/91)

I need to be able to take what is printed when you call perror
and put it into a string variable to be used in a c program.

I know that the errno variable is an external variable which points
to some internal table in the kernel called sys_errlist. 

Is there some way of getting at that table so that I can get at the 
error messages?

I tried the following logical solution after RTFM but it did not
work (it got a seg fault)

goo.c

______________________________________________________
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <sys/syslog.h>
#include <errno.h>

main()
{
extern char **sys_errlist;
FILE *fpp;
int ctr1;

fpp=fopen("/goo","w");
perror("");
printf("errno is %d\n",errno);
printf("%d\n",(int)*(sys_errlist+errno));
}

_____________________________________________________
I compiled and linkded this program with
cc -g goo.c

jik@cats.ucsc.edu (Jonathan I. Kamens) (06/13/91)

In article <212@sleepy.UUCP>, allyn@sleepy.UUCP (Mark Allyn) writes:
|> I need to be able to take what is printed when you call perror
|> and put it into a string variable to be used in a c program.

  Check if your system has a function named strerror, that takes an int
(usually errno) and returns a string.  If it has it, there should be a man
page for it.  If there is, then use it.

  If not, you'll have to use sys_errlist, as you're trying to do.  But you're
not quite doing it right....

|> I know that the errno variable is an external variable which points
|> to some internal table in the kernel called sys_errlist. 

  sys_errlist is not "some internal table in the kernel," it's an array of
strings compiled into the C library.  The other variable you need to know
about is the int sys_nerr, which contains the number of strings in
sys_errlist.  Before trying to reference a string in sys_errlist, you need to
check if errno is less than sys_nerr; if it is not, then the error does not
have a corresponding string in the sys_errlist.

|> I tried the following logical solution after RTFM but it did not
|> work (it got a seg fault)

  I see a couple of problems.

|> extern char **sys_errlist;

  This should be "extern char *sys_errlist[];".  Also, you should declare
"extern int sys_nerr;".

|> printf("%d\n",(int)*(sys_errlist+errno));

  This should be

	if (errno < sys_nerr)
		printf("%s\n", sys_errlist[errno]);
	else
		printf("Unknown error\n");

-- 
Jonathan Kamens					jik@CATS.UCSC.EDU

mouse@thunder.mcrcim.mcgill.edu (der Mouse) (06/15/91)

In article <212@sleepy.UUCP>, allyn@sleepy.UUCP (Mark Allyn) writes:

> I need to be able to take what is printed when you call perror and
> put it into a string variable to be used in a c program.

> I know that the errno variable is an external variable which points
> to some internal table in the kernel called sys_errlist.

Not in the kernel; it's part of the C library.

> Is there some way of getting at that table so that I can get at the
> error messages?

Certainly.

> I tried the following logical solution after RTFM but it did not work
> (it got a seg fault)

Let's have a look....

> #include <stdio.h>
> #include <signal.h>
> #include <sys/types.h>
> #include <sys/vfs.h>
> #include <sys/syslog.h>
> #include <errno.h>

Why in the world are you including all those things?  The only ones
that have any relevance are <stdio.h> and <errno.h> (and see my fifth
note below for a remark about the latter).

> main()
> {
> extern char **sys_errlist;

It's an array, not a pointer (try `extern char *sys_errlist[];').

> FILE *fpp;
> int ctr1;
> 
> fpp=fopen("/goo","w");
> perror("");
> printf("errno is %d\n",errno);
> printf("%d\n",(int)*(sys_errlist+errno));

First, fopen is not guaranteed to leave anything useful in errno, at
least not generally.  (Your system may make such a promise, but it is
not portable to assume so.)  It frequently will do so, but you can't
count on it.

Second, the perror and printf calls may destroy errno, since it is no
part of their contract to preserve it.  You should save it in a
variable of your own.

Third, you should check that errno is within the range [0..sys_nerr),
because the sys_errlist table may lag the system for new error codes.

Fourth, why are you casting the string from sys_errlist to an int?!

Fifth, a minor point: not all systems' <errno.h> actually declare
errno.  For portability you have to declare it yourself.  (Yes, I agree
this is a botch.  Portability sometimes means putting up with botches.)
And since this is the only thing <errno.h> does for you in this case,
you can get rid of it once you add the declaration - though in a real
program, where you use some of the Exxx values, you have to keep it.)

> }

Putting all this together, we get the following.  (Well, most of it;
this modified version still assumes that fopen() will leave something
useful in errno.  To do it right you should call open(2) directly,
followed by fdopen() if the open() succeeds and you need a FILE *.)

#include <stdio.h>

main()
{
 extern int errno;
 extern int sys_nerr;
 extern char *sys_errlist[];
 FILE *fpp;
 int save_errno;

 fpp=fopen("/goo","w");
 save_errno = errno;
 perror("");
 printf("errno is %d\n",save_errno);
 if ((save_errno >= 0) && (save_errno < sys_nerr))
    printf("%s\n",sys_errlist[save_errno]);
 else
    printf("Unknown error code %d\n",save_errno);
}

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

dold@mitisft.Convergent.COM (Clarence Dold) (06/17/91)

in article <1991Jun15.151924.24619@thunder.mcrcim.mcgill.edu>, mouse@thunder.mcrcim.mcgill.edu (der Mouse) says:
...
> Third, you should check that errno is within the range [0..sys_nerr),
> because the sys_errlist table may lag the system for new error codes.
...
>  printf("errno is %d\n",save_errno);
>  if ((save_errno >= 0) && (save_errno < sys_nerr))
>     printf("%s\n",sys_errlist[save_errno]);
>  else
>     printf("Unknown error code %d\n",save_errno);
> }

printf("Errno %d: %s\n", want_err, 
	want_err <= sys_nerr ? sys_errlist[want_err] : "Out of range" );

I like this one because it's one of the few places where the " ? : "
construct looks correct to me.

-- 
---
Clarence A Dold - dold@tsmiti.Convergent.COM
               ...pyramid!ctnews!tsmiti!dold

boyd@prl.dec.com (Boyd Roberts) (06/17/91)

In article <2157@mitisft.Convergent.COM>, dold@mitisft.Convergent.COM (Clarence Dold) writes:
> printf("Errno %d: %s\n", want_err, 
> 	want_err <= sys_nerr ? sys_errlist[want_err] : "Out of range" );
> 
> I like this one because it's one of the few places where the " ? : "
> construct looks correct to me.

Eh?  The bounds of a reasonable errnos lie between 1 and sys_nerr.
`errno == 0' means no error.

Boyd Roberts			boyd@prl.dec.com

``When the going gets wierd, the weird turn pro...''

gwyn@smoke.brl.mil (Doug Gwyn) (06/17/91)

In article <1991Jun17.124318.1384@prl.dec.com> boyd@prl.dec.com (Boyd Roberts) writes:
-In article <2157@mitisft.Convergent.COM>, dold@mitisft.Convergent.COM (Clarence Dold) writes:
-> printf("Errno %d: %s\n", want_err, 
-> 	want_err <= sys_nerr ? sys_errlist[want_err] : "Out of range" );
-> I like this one because it's one of the few places where the " ? : "
-> construct looks correct to me.
-Eh?  The bounds of a reasonable errnos lie between 1 and sys_nerr.
-`errno == 0' means no error.

Which is probably why sys_errlist[0] contains "Error 0".

jik@cats.ucsc.edu (Jonathan I. Kamens) (06/18/91)

In article <2157@mitisft.Convergent.COM>, dold@mitisft.Convergent.COM (Clarence Dold) writes:
|> printf("Errno %d: %s\n", want_err, 
|> 	want_err <= sys_nerr ? sys_errlist[want_err] : "Out of range" );

The "<=" should be "<".  Sys_nerr records the number of elements in the
sys_errlist array, which means the highest valid index in the array is
sys_nerr-1, and the lowest is 0.

-- 
Jonathan Kamens					jik@CATS.UCSC.EDU

dold@mitisft.Convergent.COM (Clarence Dold) (06/18/91)

in article <1991Jun17.124318.1384@prl.dec.com>, boyd@prl.dec.com (Boyd Roberts) says:

> In article <2157@mitisft.Convergent.COM>, dold@mitisft.Convergent.COM (Clarence Dold) writes:
>> printf("Errno %d: %s\n", want_err, 
>> 	want_err <= sys_nerr ? sys_errlist[want_err] : "Out of range" );

> Eh?  The bounds of a reasonable errnos lie between 1 and sys_nerr.
> `errno == 0' means no error.

But sys_errlist[0] does contain a string, so it shouldn't be excluded,
unless you especially like to have extra instructions in your code.
-- 
---
Clarence A Dold - dold@tsmiti.Convergent.COM
               ...pyramid!ctnews!tsmiti!dold

torek@elf.ee.lbl.gov (Chris Torek) (06/19/91)

>In article <212@sleepy.UUCP>, allyn@sleepy.UUCP (Mark Allyn) writes:
>>I need to be able to take what is printed when you call perror
>>and put it into a string variable to be used in a c program.

In article <16982@darkstar.ucsc.edu> jik@cats.ucsc.edu
(Jonathan I. Kamens) writes:
>  Check if your system has a function named strerror, that takes an int
>(usually errno) and returns a string. ... If not, you'll have to use
>sys_errlist ...

In fact, if you do not have strerror(), you should write it in terms
of sys_errlist:

>	if (errno < sys_nerr)
>		printf("%s\n", sys_errlist[errno]);
>	else
>		printf("Unknown error\n");

Better:

#include <stdio.h>

char *
strerror(err)
	register int err;
{
	extern char *sys_errlist[];
	extern int sys_nerr;
	static char unknown[19+40+1];
			/* 19: strlen("Unknown error code ") +
			   40: max length of 128 bit int in decimal
			       (-170141183460469231731687303715884105728) +
			   1: final '\0' */

	if ((unsigned)err < sys_nerr)
		return (sys_errlist[err]);
	(void) sprintf(unknown, "Unknown error code %d", err);
	return (unknown);
}
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

dcoskun@alias.com (Denis Coskun) (06/20/91)

In <17138@darkstar.ucsc.edu> jik@cats.ucsc.edu (Jonathan I. Kamens) writes:
> Sys_nerr records the number of elements in the sys_errlist array,
> which means the highest valid index in the array is sys_nerr-1,
> and the lowest is 0.

While I agree that this makes sense, is this really established practice
or standardized?  I ask because SGIs (Irix 3.3.2) have messages for indices
0 through sys_nerr.  This program,

   #include <stdio.h>
   #include <errno.h>
   extern int sys_nerr;
   extern char *sys_errlist[];

   main()
   {
       printf("ENFSREMOTE = %d\n", ENFSREMOTE);
       printf("sys_nerr   = %d\n", sys_nerr);
       printf("sys_errlist[sys_nerr] = `%s'\n", sys_errlist[sys_nerr]);
   }

gives the following output:

   ENFSREMOTE = 135
   sys_nerr   = 135
   sys_errlist[sys_nerr] = `Too many levels of remote in path'

--
Denis Coskun    Alias Research Inc. Toronto Canada    dcoskun@alias.com

jik@cats.ucsc.edu (Jonathan I Kamens) (06/22/91)

In article <1991Jun19.215654.18120@alias.com>, dcoskun@alias.com (Denis Coskun) writes:
|> In <17138@darkstar.ucsc.edu> jik@cats.ucsc.edu (Jonathan I. Kamens) writes:
|> > Sys_nerr records the number of elements in the sys_errlist array,
|> > which means the highest valid index in the array is sys_nerr-1,
|> > and the lowest is 0.
|> 
|> While I agree that this makes sense, is this really established practice
|> or standardized?  I ask because SGIs (Irix 3.3.2) have messages for indices
|> 0 through sys_nerr.

Well, here's my test program:

main()
{
     extern int sys_nerr;
     extern char *sys_errlist[];

     printf("sys_nerr = %d\n", sys_nerr);
     printf("sys_errlist[sys_nerr] = 0x%x\n", sys_errlist[sys_nerr]);
     printf("sys_errlist[sys_nerr] = \"%s\"\n", sys_errlist[sys_nerr]);
}

It produces the following on an IBM RT/PC running AOS 4.3:

sys_nerr = 76
sys_errlist[sys_nerr] = 0x4c
sys_errlist[sys_nerr] = "1
                          h__"

It produces the following on a VAX running BSD 4.3:

sys_nerr = 76
sys_errlist[sys_nerr] = 0x4c
sys_errlist[sys_nerr] = "}"

It produces the following on a DECstation 3100 running Ultrix 3.1:

sys_nerr = 75
sys_errlist[sys_nerr] = 0x20746f4e
Segmentation violation (core dumped)

It produces the following on the NeXT machine:

sys_nerr = 84
sys_errlist[sys_nerr] = 0x0
sys_errlist[sys_nerr] = "(null pointer)"

It produces the following on an IBM PS/2 running AIX 1.2:

sys_nerr = 109
sys_errlist[sys_nerr] = 0x6d
sys_errlist[sys_nerr] = "!"

It produces the following on an i386 machine running SysVr4:

sys_nerr = 152
sys_errlist[sys_nerr] = 0x98
sys_errlist[sys_nerr] = ""

It produces the following on a Mac running A/UX:

sys_nerr = 103
sys_errlist[sys_nerr] = 0x67
sys_errlist[sys_nerr] = "d"

It produces the following on a Sparc running SunOS 4.1.1:

sys_nerr = 91
sys_errlist[sys_nerr] = 0x5b
Segmentation fault

In case it's not obvious by now :-), what I'm trying to imply is that Irix
3.3.2 is probably wrong here.

-- 
Jonathan Kamens					jik@CATS.UCSC.EDU