0

I'm making a simple HTTP webserver, and this is the relevant portion of the code:

179         stat(path, &rd_perm); /* path contains the full path to the original file request */
180         if(errno == EACCES){ /* We don't have permissions to view the file... */
181                 printf("======> NO READ PERMISSIONS FOR FILE!\n"); /* sanity check */
182                 /* Send the client a 403 Forbidden */
183
184                 send(clients[n], "HTTP/1.0 403 Forbidden\r\n\r\n", 26, 0);
185                 strcpy(path, home_dir);
186                 strcpy(&path[strlen(home_dir)], FORBIDDEN);
187
188                 /* After changing the pathname to the "forbidden" page, write that page to the client socket. */
189
190                 if( (fd = open(path, O_RDONLY)) != -1){
191                         while( (bytes_read = read(fd, data_to_send, 1024)) > 0){
192                                 write(clients[n], data_to_send, bytes_read);
193                         }
194                 }
195         }

I thought this would be enough, but it's not. I can see two things being wrong here:

  • My entry condition, errno == EACCES. Since this is a multiprocess webserver, would this be a "safe" and correct way of checking errno's value? Is there a better way?
  • Do I still need to send() a "301 Moved Permanently\nLocation: "? This seems wrong, seeing how what I wanted wasn't moved at all - I just didn't have access to it.

I'd wager that my error is the first one, as it doesn't seem to go into the if at all. Any suggestions would be appreciated.

filpa
  • 3,651
  • 8
  • 52
  • 91
  • 1
    Multi-*process* or multi-*thread*? – Some programmer dude Oct 03 '13 at 14:06
  • 1
    Also, while plain newline works, the specification actually specifies a carriage-return/newline pair. – Some programmer dude Oct 03 '13 at 14:07
  • 2
    Oh, and never, ever check `errno` unless the function you call actually fails. If the `stat` function doesn't return `-1` then the value of `errno` is undefined. – Some programmer dude Oct 03 '13 at 14:09
  • By the way, on Linux there is a [`sendfile`](http://linux.die.net/man/2/sendfile) system call. Exists (in slightly different form) on [BSD as well](http://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2) (which means that [Apple OSX also have it](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)). – Some programmer dude Oct 03 '13 at 14:14
  • 1
    And unless you need anything else from the `stat` call, you might as well use `open` to check for access the same way, thereby having one system call less to make. – Some programmer dude Oct 03 '13 at 14:16
  • @JoachimPileborg Multi-process. I'll update the return to \r\n, thanks. As for open(), it was my understanding that open() returns the same error value for `file not found` and `permission denied`, so I wanted to differentiate between the two. – filpa Oct 03 '13 at 14:18
  • 1
    If the file doesn't exist you would get an `ENOENT` error in both cases. – Some programmer dude Oct 03 '13 at 14:20
  • 1
    And if it's multi-process, then each process have its own `errno`. I actually think even threads have their own separate `errno`. Ah yes it is, see [this old question](http://stackoverflow.com/questions/1694164/is-errno-thread-safe). – Some programmer dude Oct 03 '13 at 14:22
  • Ah, you are correct! It is `ENOENT` for not found, and `EACCES` for no permissions. I must have misread the man page. I'll attempt to do it that way then. Still, the question remains: should I simply send a `HTTP/1.x 403 Forbidden' and let the browser sort it out? Or actuall redirect to a 403 page? – filpa Oct 03 '13 at 14:23
  • 1
    As for the 403 error, I think it would be more correct to send an 403 error code and let the client handle it. – Some programmer dude Oct 03 '13 at 14:25
  • Great! Using just `open()` (as opposed to using `stat()` as well), I was able to get close the desired behaviour. If you wish, you can post your answer as well. If not, I will accept the one that @alk posted as it is similar and could have led me to a similar conclusion. – filpa Oct 03 '13 at 14:35

1 Answers1

0

errno will only by set if stat() failed.

So modifiy your code like this:

    int result = stat(path, &rd_perm); /* path contains the full path to the original file request */
    if ((-1 == result) && (errno == EACCES)) { /* We don't have permissions to view the file... */

Each process has its own instance of errno.

If going multihreaded make sure to compile using option -pthread to have errno being declared as thread-local.

Please note that the compiler option -pthread is different from telling the linker to link against libpthread by giving the linker-option -lpthread.

alk
  • 69,737
  • 10
  • 105
  • 255