# How to handle POSIX errors

**URL:** <https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753>\
**Category:** Help!\
**Created:** [March 21, 2021, 5:03pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753 "2021-03-21T17:03:43Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 21, 2021, 5:03pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/1 "2021-03-21T17:03:43Z")

</div>

Hi.

I’m writing a foreign C function for the realpath(3) system library call. I want to do it right, so I try to handle any errors correctly.

realpath(3) returns errors via the errno variable. For ENOENT (file not found) errors, I’ve found the existence\_error() exception in the description of absolute\_file\_name. It doesn’t seem to be documented, though, what existence\_error(A,B) means. Anyway, I’m throwing the same as what absolute\_file\_name throws. (But what is a “source\_sink”?)

But there are a couple more errors, which I want to handle correctly too, such as EACCES and ENOTDIR. How should those be handled? Are there more standardised exceptions? Is there an exception, which carries an errno value? Where are the errors and exceptions documented?

I plan to post the results here, when I’m finished.

Cheers,  
Volker

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 7:57am UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/2 "2021-03-22T07:57:31Z")

</div>

> [@volker-wysk](#):
>
> I’m writing a foreign C function for the realpath(3) system library call.

Note that this is more or less doing what absolute\_file\_name/2,3 is doing. The main difference is that this predicate doesn’t work up to the root, but up to a table of previously stored canonical directories and does some seeding in this, notably to accept $HOME as a canonical dir, regardless of whether it in some symlinked position or not.

As for errors, internally there is PL\_error() that has an `ERR_ERRNO, errno` argument option that will try to map some of the POSIX errors to related Prolog errors. This isn’t easy though and thus mainly used for the general errors such as ENOMEM. For example for EPERM you need the object acted upon and the desired action. Many of the POSIX errors have no obvious Prolog equivalent (EINTR, EINVAL, …) As a result, most of the internal code handles the scenarios that dictate a clean Prolog error special and use the above for any other POSIX error, resulting in some pretty general error that more or less states something went wrong at the OS level ☹ If you want to investigate this and come up with a good API to improve on this, please contribute!

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 22, 2021, 2:04pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/3 "2021-03-22T14:04:21Z")

</div>

> [@jan](#):
>
> > [@volker-wysk](#):
> >
> > I’m writing a foreign C function for the realpath(3) system library call.
> 
> Note that this is more or less doing what [absolute\_file\_name/2](https://www.swi-prolog.org/pldoc/doc_for?object=absolute_file_name/2),3 is doing. The main difference is that this predicate doesn’t work up to the root, but up to a table of previously stored canonical directories and does some seeding in this, notably to accept $HOME as a canonical dir, regardless of whether it in some symlinked position or not.

I need to expand the symlinks in some paths. I’ve tried, and absolute\_file\_name/3 doesn’t do that. realpath is the standard function for this (at least in the non-Prolog world). So I thought I’d add realpath/2 as a foreign predicate.

> [@jan](#):
>
> As for errors, internally there is PL\_error() that has an `ERR_ERRNO, errno` argument option that will try to map some of the POSIX errors to related Prolog errors.

I found it in the SWI-Prolog sources in src/pl-error.c, but there is no ERR\_ERRNO mentioned anywhere in that file.

And since it’s interal to SWI-Prolog, how should you use it? Do you mean to copy it out of pl-errno.c and include it in the source code of the realpath/2, which I’m trying to write?

> [@jan](#):
>
> This isn’t easy though and thus mainly used for the general errors such as ENOMEM. For example for EPERM you need the object acted upon and the desired action. Many of the POSIX errors have no obvious Prolog equivalent (EINTR, EINVAL, …) As a result, most of the internal code handles the scenarios that dictate a clean Prolog error special and use the above for any other POSIX error, resulting in some pretty general error that more or less states something went wrong at the OS level ☹ If you want to investigate this and come up with a good API to improve on this, please contribute!

I don’t quite get it. What is a “Prolog error special”?

What kind of API do you have in mind? A function which replaces PL\_error() and is included in the Foreign Language Interface?

Cheers,  
Volker

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 2:34pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/4 "2021-03-22T14:34:33Z")

</div>

> [@volker-wysk](#):
>
> I need to expand the symlinks in some paths. I’ve tried, and [absolute\_file\_name/3](https://www.swi-prolog.org/pldoc/doc_for?object=absolute_file_name/3) doesn’t do that.

Maybe read\_link/3?

> [@volker-wysk](#):
>
> but there is no ERR\_ERRNO mentioned anywhere in that file.

Sorry. There is MSG\_ERRNO that causes the message comment to be set to the strerror() output. A grep through the source will tell you how that is used. It is far from ideal …

> [@volker-wysk](#):
>
> Prolog error special

Typo ☹

> [@volker-wysk](#):
>
> What kind of API do you have in mind? A function which replaces PL\_error() and is included in the Foreign Language Interface?

I guess ideally we have something like

```
PL_errno_error(int errno, ...)

```

The problem is the …, That should give enough info such that for e.g. open() we can map EPERM to permission\_error(open, source\_sink, File), i.e. we must pass the operation and the think the operation works on. Different POSIX errors require different additional information. Possible there is a quite small set of additional information that allows generating the correct Prolog errors. Most of the OS errors are some sort of existence, resource, representation or permission error.

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 22, 2021, 3:23pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/5 "2021-03-22T15:23:44Z")

</div>

> [@jan](#):
>
> > [@volker-wysk](#):
> >
> > I need to expand the symlinks in some paths. I’ve tried, and [absolute\_file\_name/3](https://www.swi-prolog.org/pldoc/doc_for?object=absolute_file_name/3) doesn’t do that.
> 
> Maybe [read\_link/3](https://www.swi-prolog.org/pldoc/doc_for?object=read_link/3)?

This is the documentation for read\_link/3:

“If File points to a symbolic link, unify Link with the value of the link and Target to the file the link is pointing to. Target points to a file, directory or non-existing entry in the file system, but never to a link. Fails if File is not a link. Fails always on systems that do not support symbolic links.”

I find it’s not so clear. Does “but never to a link” mean “but never to a symlink”?

Does, what is done with Target mean, that any symlinks are followed, until the symlink target no longer is a symlink?

read\_link/3 doesn’t expand symlinks in the beginning or middle of a path. It only reads (Link argument) and expands (Target argument), symlinks in the last path component. What I need is exactly readlink(3).

Thanks tough,  
Volker

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 3:43pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/6 "2021-03-22T15:43:14Z")

</div>

> [@volker-wysk](#):
>
> I find it’s not so clear. Does “but never to a link” mean “but never to a symlink”?
> 
> Does, what is done with Target mean, that any symlinks are followed, until the symlink target no longer is a symlink?

If is a simple loop that, as long as the path points at the symlink, reads the link and determines the new target (notably if the link is relative). So, in the end you have the location the link points at. It is intended to perform safe atomic updates of a file’s content and ensure the real file is updated rather than a link being replaced by a file. Thus, you deference the link, create a new file in the same directory and when complete rename the new file to the old one.

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 22, 2021, 3:54pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/7 "2021-03-22T15:54:38Z")

</div>

> [@jan](#):
>
> I guess ideally we have something like
> 
> ```prolog
> PL_errno_error(int errno, ...)
> 
> ```
> 
> The problem is the …, That should give enough info such that for e.g. open() we can map EPERM to permission\_error(open, source\_sink, File), i.e. we must pass the operation and the think the operation works on. Different POSIX errors require different additional information. Possible there is a quite small set of additional information that allows generating the correct Prolog errors. Most of the OS errors are some sort of existence, resource, representation or permission error.

The additional arguments (the “…”) could be made use of, like in PL\_unify\_term. A marker, for instance EI\_FILENAME (“EI” for “Error Information”), could be used, with the value following. An end marker (EI\_END) would end the list of pieces of error information.

Such as this:

```prolog
PL_errno_error(errno, EI_HANDLE, my_file_handle, EI_FILENAME, my_filename, EI_END);

```

The PL\_errno\_errorr function would only use the error information pieces which it needs, depending on the errno value. So it simply can be called when a POSIX error occured, and it isn’t necessary make the call dependent on the errno, providing different pieces of information for each case.

Is there any documentation, about the error values (such as existence\_error() and permission\_error()) that SWI-Prolog uses? And still: what is a “source\_sink”?

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 22, 2021, 3:58pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/8 "2021-03-22T15:58:55Z")

</div>

> [@](#):
>
> It is a simple loop that, as long as the path points at the symlink, reads the link and determines the new target (notably if the link is relative). So, in the end you have the location the link points at. It is intended to perform safe atomic updates of a file’s content and ensure the real file is updated rather than a link being replaced by a file. Thus, you deference the link, create a new file in the same directory and when complete rename the new file to the old one.

That’s what I guessed. Could you please write it into the documentation of read\_link/3?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 4:08pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/9 "2021-03-22T16:08:26Z")

</div>

EI\_\* could be an option. Another option might be a struct, so you do

```
error_info ei = {.type = "source_sink", .name = name, .op = "open"};
PL_errno_error(errno, &ei);

```

> [@volker-wysk](#):
>
> Is there any documentation, about the error values (such as existence\_error() and permission\_error()) that [SWI-Prolog](https://www.swi-prolog.org/pldoc/doc_for?object=manual) uses?

The basics are the ISO errors. There are a few generic extensions. You find these in library(error). Next there are a lot of specific errors. One source is boot/messages.pl and the other src/pl-error.c

> [@volker-wysk](#):
>
> And still: what is a “source\_sink”?

The great ISO term for a file 🙂 Well, I think the authors meant something more general, but in practice it is mostly that. I general I’d say things on which open/4 works.

> [@volker-wysk](#):
>
> Could you please write it into the documentation of [read\_link/3](https://www.swi-prolog.org/pldoc/doc_for?object=read_link/3)?

I have some hope people will send PRs for these things …

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 22, 2021, 5:18pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/10 "2021-03-22T17:18:24Z")

</div>

> [@jan](#):
>
> EI\_\* could be an option. Another option might be a struct, so you do
> 
> ```prolog
> error_info ei = {.type = "source_sink", .name = name, .op = "open"};
> PL_errno_error(errno, &ei);
> 
> ```

I didn’t know that syntax (“.type = …”). I tried it and it seems like non-mentioned members are set to null. Is that the case? If so, I think I like your approach better, since it is type safe.

> [@jan](#):
>
> > [@volker-wysk](#):
> >
> > Could you please write it into the documentation of [read\_link/3](https://www.swi-prolog.org/pldoc/doc_for?object=read_link/3)?
> 
> I have some hope people will send PRs for these things …

PR = pull request ?

Bye,  
Volker

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 22, 2021, 6:42pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/11 "2021-03-22T18:42:06Z")

</div>

> [@volker-wysk](#):
>
> I tried it and it seems like non-mentioned members are set to null. Is that the case?

Yes. AFAIK, if you just declare a struct var in C it is not initialised, but if you initialise one field it sets the rest of the struct to 0. That is why you can normally use `struct mystruct myvar = {0}` to get a zero-initialized struct regardless of the fields.

> [@volker-wysk](#):
>
> PR = pull request ?

Yip. Anything else works as well, but pull requests are pretty efficient to deal with on both ends when you get used to the approach.

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 23, 2021, 4:14pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/12 "2021-03-23T16:14:45Z")

</div>

Hi!

> [@jan](#):
>
> As for errors, internally there is PL\_error() that has an `ERR_ERRNO, errno` argument option that will try to map some of the POSIX errors to related Prolog errors. This isn’t easy though and thus mainly used for the general errors such as ENOMEM. For example for EPERM you need the object acted upon and the desired action. Many of the POSIX errors have no obvious Prolog equivalent (EINTR, EINVAL, …) As a result, most of the internal code handles the scenarios that dictate a clean Prolog error special and use the above for any other POSIX error, resulting in some pretty general error that more or less states something went wrong at the OS level ![:frowning:](https://emoji.discourse-cdn.com/twitter/frowning.png?v=9 ":frowning:") If you want to investigate this and come up with a good API to improve on this, please contribute!

I don’t understand what you have in mind. Should I hack the SWI-Prolog source code?

I don’t know why PL\_error() isn’t made available as a part of the Foreign Language Interface. Or do you have in mind to let me add a frontend to PL\_error()?

I still don’t know what you mean by “Prolog error special”.

Cheers,  
Volker

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 23, 2021, 5:11pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/13 "2021-03-23T17:11:02Z")

</div>

> [@volker-wysk](#):
>
> I don’t understand what you have in mind. Should I hack the [SWI-Prolog](https://www.swi-prolog.org/pldoc/doc_for?object=manual) source code?

Eventually, if you want to contribute a better way of reporting OS errors, this is what needs to be done 🙂

> [@volker-wysk](#):
>
> I don’t know why PL\_error() isn’t made available as a part of the Foreign Language Interface.

It was intended for that. However, the interface is not type-safe and develops too much with changes to the internals, so making it public is asking for trouble.

It appears that most people consider throwing exceptions too hard. That is why I added `PL_type_error()`, etc. as well as the automatic exceptions from e.g. `PL_get_atom_ex()`. Extending this with something that is easy to raise a fair error from an arbitrary system call would be nice to have. Once we have that we probably want to generalize it. Many libraries come with an `errno`/`strerror()` style error API. It would be great if you could supply a table (as an array of C structs) that provide a mapping from the error codes to Prolog errors. Right now I’m typically lazy and map such errors to e.g. odbc\_error(Code, Message).

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 25, 2021, 12:11pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/14 "2021-03-25T12:11:02Z")

</div>

Hi!

> [@jan](#):
>
> It would be great if you could supply a table (as an array of C structs) that provide a mapping from the error codes to Prolog errors. Right now I’m typically lazy and map such errors to e.g. odbc\_error(Code, Message).

I have a first draft of how such a table could look like, and how it would be used. But Discourse seems to not allow appending a file to a post.

Perhaps we should discuss the rest by private mail. There are some points which need to be clarified.

Cheers,  
Volker

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [March 25, 2021, 12:56pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/15 "2021-03-25T12:56:27Z")

</div>

> [@volker-wysk](#):
>
> [Discourse](https://www.discourse.org) seems to not allow appending a file to a post.

Discourse restricts file uploads based on file type.

Currently

 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/7/79c63480aef5a2a2b3a170a401be184526035f10.png)

What was the file type you used?

---

<div class="post-metadata">

**Author:** ![volker-wysk](https://avatars.discourse-cdn.com/v4/letter/v/858c86/32.png) [@volker-wysk](https://swi-prolog.discourse.group/u/volker-wysk)\
**Post date:** [March 25, 2021, 1:11pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/16 "2021-03-25T13:11:00Z")

</div>

> [@EricGT](#):
>
> What was the file type you used?

It was “.c”.

Volker

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 25, 2021, 1:34pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/17 "2021-03-25T13:34:22Z")

</div>

Thanks for taking the time to look at this. Continue as you seem fit, either private mail, github or when .c is allowed on this forum.

@EricGT: I see little reason to reject extensions from programming languages that are used here.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [March 25, 2021, 2:17pm UTC](https://swi-prolog.discourse.group/t/how-to-handle-posix-errors/3753/18 "2021-03-25T14:17:28Z")

</div>

@volker-wysk @jan

Updated for file type of `c`. Let me know if it does not work.  
If other file types are needed for programming languages just let us know.  
If you ask for something like the `zip` file type then the admins would need to discuss it (think security concerns).

Thanks.

 ![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/8/8ec384911c700736686b5e2c0160f7b9fb19e7ca.png)
