modules/up/src/Core/sys/File.cc

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. private_strerror
  2. File
  3. File
  4. setHandlers
  5. read
  6. write
  7. DatagramSocket
  8. DatagramSocket
  9. recvFrom
  10. sendTo
  11. defaultInterface
  12. RawSocket
  13. RawSocket
  14. recvFrom
  15. sendTo
  16. join
  17. MulticastSocket
  18. MulticastSocket
  19. defaultInterface
  20. setTTL
  21. StreamSocket
  22. StreamSocket
  23. StreamSocket
  24. ListenSocket
  25. ListenSocket
  26. accept
  27. DiskFile
  28. DiskFile
  29. seekTo
  30. seekEnd
  31. lock
  32. truncate
  33. createDir
  34. openPidFile
  35. closePidFile
  36. daemonize

//
// $Id: File.cc,v 1.1.1.1 2000/03/10 16:32:20 engin Exp $
//
// system.cc
// Author: Ramesh Govindan <govindan@isi.edu>
//
// Abstracted OS facilities for file system access and
// for communication primitives. This file contains implementations of:
//      - network addresses (Address class)
//      - OS file descriptors and descriptor sets
//      - a common time representation
//

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <cstdio>
#include <cstdlib>
#include <cerrno>

extern "C" {
#if HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/file.h>    
#include <sys/resource.h>
#include <sys/termios.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

#ifdef HOST_OS_IS_SOLARIS
#include <sys/systeminfo.h>
#include <netdb.h>
#endif    
}

#include "util/Types.hh"
#include "util/List.hh"
#include "util/Handler.hh"
#include "util/Trail.hh"

#include "sys/Address.hh"
#include "sys/File.hh"
#include "sys/Signal.hh"
#include "sched/Dispatcher.hh"
#include "network/Headers.hh"

// Added by wlee to port to FreeBSD and BSDI
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK         (u_long)0x7F000001
#endif

extern "C" {
#ifndef STDC_HEADERS
extern int socket(...);
extern int bind(...);
extern int connect(...);
extern int read(...);
extern int write(...);
extern int setsockopt(...);
extern int getsockname(...);
extern int close(...);
extern int recvfrom(...);
extern int sendto(...);
extern int listen(...);
extern int accept(...);
extern off_t lseek(...);
extern int flock(...);
extern int ftruncate(...);
extern int stat(...);
#endif
}

// For printing out system error messages: from GNU textutils lib
#if HAVE_STRERROR
#ifndef strerror
extern "C" char *strerror(int);
#endif
#else
extern int sys_nerr;
extern char* sys_errlist[];

static char*
private_strerror(int errnum)
/* [<][>][^][v][top][bottom][index][help] */
{
    if (errnum > 0 && errnum <= sys_nerr)
        return sys_errlist[errnum];
    return "Unknown system error";
}
#define strerror private_strerror
#endif // HAVE_STRERROR

#ifdef ACCEPT_USES_SOCKLEN_T
#define SOCKLEN_T socklen_t
#elif ACCEPT_USES_SIZE_T
#define SOCKLEN_T size_t
#else
#define SOCKLEN_T int 
#endif

// File local variables
static TraceCode        traceFile("file");
static Handler          nullHandler(NULL, NULL);
static int              pidDescriptor = -1;

File::File(int fd,
/* [<][>][^][v][top][bottom][index][help] */
           FileMode m,
           const Handler& rh,
           const Handler& wh)
        : ListNode()
{
    descriptor_ = fd;
    mode_ = m;

    if (descriptor_ < 0) {
        FATAL("couldn't open file descriptor: %s\n",
              strerror(errno));
        // NotReached
    }
    
    readHandler = rh;
    writeHandler = wh;
    dispatcher.files.inset(this);
}

File::~File()
/* [<][>][^][v][top][bottom][index][help] */
{
    TRACE(traceFile,
          "closing file %d\n",
          descriptor_);
    close(descriptor_);
    dispatcher.files.outset(this);
}

void
File::setHandlers(Handler& rh,
/* [<][>][^][v][top][bottom][index][help] */
                  Handler& wh)
{
    dispatcher.files.outset(this);
    readHandler = rh;
    writeHandler = wh;
    dispatcher.files.inset(this);
}

int
File::read(char *buffer,
/* [<][>][^][v][top][bottom][index][help] */
           int  bufferLength)
{
    int retval;
    
    if (mode_ != FileModeReadOnly
        && mode_ != FileModeReadWrite) {
        ERROR("illegal attempt to read from descriptor\n");
        return FileOpHardError;
    }
    
    TRACE(traceFile,
          "read file %d length %d\n",
          descriptor_,
          bufferLength);
    
    retval = ::read(descriptor_,
                    buffer,
                    bufferLength);
    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
            case EINTR:
                return FileOpSoftError;
            default:
                return FileOpHardError;
        }
    }
    return retval;
}

int
File::write(char *buffer,
/* [<][>][^][v][top][bottom][index][help] */
            int  bufferLength)
{
    int retval;
    
    if (mode_ != FileModeReadWrite) {
        ERROR("attempt to write on read-only file descriptor\n");
        return FileOpHardError;
    }
    
    TRACE(traceFile,
          "write file %d length %d\n",
          descriptor_,
          bufferLength);
    
    retval = ::write(descriptor_,
                     buffer,
                     bufferLength);
    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
            case EINTR:
                return FileOpSoftError;
            default:
                return FileOpHardError;
        }
    }
    return retval;
}

DatagramSocket::DatagramSocket(const Handler& rh,
/* [<][>][^][v][top][bottom][index][help] */
                               const Handler& wh)
        : File(::socket(AF_INET, SOCK_DGRAM, 0),
               FileModeReadWrite,
               rh,
               wh)
{
    // Empty
}

DatagramSocket::~DatagramSocket()
/* [<][>][^][v][top][bottom][index][help] */
{
    // Empty
}

int
DatagramSocket::recvFrom(char* buffer,
/* [<][>][^][v][top][bottom][index][help] */
                         int bufferLength,
                         Address& addr,
                         Port *port)
{
    struct sockaddr_in sin;
    int retval;
    size_t socklen = sizeof(sin);
    
    retval = ::recvfrom(descriptor_,
                        buffer,
                        bufferLength,
                        0,
                        (struct sockaddr *) &sin,
                        (SOCKLEN_T*)&socklen);

    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
            case EINTR:
                return FileOpSoftError;
            default:
                return FileOpHardError;
        }
    }

    addr.set((U32) ntohl(sin.sin_addr.s_addr));
    *port = ntohs(sin.sin_port);
    TRACE(traceFile,
          "read %d bytes on socket %d from %s\n",
          retval,
          descriptor_,
          addr.name());
    return retval;
}

int
DatagramSocket::sendTo(char* buffer,
/* [<][>][^][v][top][bottom][index][help] */
                       int bufferLength,
                       Address& addr,
                       Port port)
{
    struct sockaddr_in sin;
    int retval;

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(addr.get());
    sin.sin_port = htons(port);
    retval = ::sendto(descriptor_,
                      buffer,
                      bufferLength,
                      0,
                      (struct sockaddr *) &sin,
                      sizeof(sin));
    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
            case EINTR:
                return FileOpSoftError;
            default:
                return FileOpHardError;
        }
    }

    TRACE(traceFile,
          "wrote %d bytes on socket %d from %s\n",
          retval,
          descriptor_,
          addr.name());
    
    return retval;
}

int
DatagramSocket::defaultInterface(Address& localAddress, const Address &dst) {
/* [<][>][^][v][top][bottom][index][help] */
#ifndef ALLOW_MULTIPLE_IFACES
        static bool hashed= false;
        static Address myOnlyIface;
        if (hashed) 
                localAddress.set(myOnlyIface.get());
        return FileOpOK;
#endif
    int sock;
    struct sockaddr_in sin;
    size_t socklen = sizeof(sin);

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(dst.get());
    //xxx: sin.sin_port = htons(port);          // We can then use write
    
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        ERROR("couldn't open socket for getting local address: %s\n",
              strerror(errno));
        return FileOpHardError;
    }

    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
        ERROR("couldn't connect for getting local address: %s\n",
              strerror(errno));
        return FileOpHardError;
    }

    if (getsockname(sock, 
                    (struct sockaddr *) &sin, 
                    (SOCKLEN_T*)&socklen) < 0) {
        ERROR("couldn't get local address: %s\n",
              strerror(errno));
        return FileOpHardError;
    }
    
#ifdef HOST_OS_IS_SOLARIS
    if (sin.sin_addr.s_addr == 0) {
        char myhostname[256];
        struct hostent *hp;
        int error;
    
        error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
        if (error == -1) {
            ERROR("failed on sysinfo: %s\n",
                  strerror(errno));
            exit(-1);
        }

        hp = gethostbyname(myhostname);
        if (hp == NULL || hp->h_addrtype != AF_INET ||
            hp->h_length != sizeof(sin.sin_addr)) {
            ERROR("failed on gethostbyname: %s\n",
                  strerror(errno));
            exit(-1);
        }
        memcpy((char *)&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
    }
#endif

    localAddress.set(ntohl(sin.sin_addr.s_addr));
    close(sock);
    TRACE(traceFile,
          "default interface for dst %s is %s\n",
          dst.name(),
          localAddress.name());
#ifndef ALLOW_MULTIPLE_IFACES
    myOnlyIface= localAddress.get();
    hashed= true;
#endif
    return FileOpOK;
}       

RawSocket::RawSocket(Handler& rh,
/* [<][>][^][v][top][bottom][index][help] */
                     Handler& wh,
                     RawSocketType type)
        : File(::socket(AF_INET, SOCK_RAW, type),
               FileModeReadWrite,
               rh,
               wh)
{
    int bufSize = 48*1024;
    int bval = 1;

#ifdef  IP_HDRINCL
    if(setsockopt(descriptor_, IPPROTO_IP, IP_HDRINCL,
                  (char *)&bval, sizeof(bval)) < 0) {
        FATAL("setsockopt IP_HDRINCL %u", bval);
        // Not Reached
    }
#endif
    if(setsockopt(descriptor_, SOL_SOCKET, SO_RCVBUF, (char *)&bufSize,
                  sizeof(bufSize)) < 0) {
        FATAL("setsockopt SO_RCVBUF %u", bufSize);
        // Not Reached
    }
}

RawSocket::~RawSocket()
/* [<][>][^][v][top][bottom][index][help] */
{
    // Empty
}

int
RawSocket::recvFrom(char* buffer,
/* [<][>][^][v][top][bottom][index][help] */
                    int bufferLength,
                    Address& addr,
                    Port *port)
{
    struct sockaddr_in sin;
    int retval;
    size_t socklen = sizeof(sin);
    
    retval = ::recvfrom(descriptor_,
                        buffer,
                        bufferLength,
                        0,
                        (struct sockaddr *) &sin,
                        (SOCKLEN_T*)&socklen);
    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
            case EINTR:
                return FileOpSoftError;
            default:
                return FileOpHardError;
        }
    }

    addr.set((U32) ntohl(sin.sin_addr.s_addr));
    *port = ntohs(sin.sin_port);
    TRACE(traceFile,
          "read %d bytes on raw socket %d from %s\n",
          retval,
          descriptor_,
          addr.name());
#ifdef HOST_OS_IS_FREEBSD
    IP* iph= (IP*) buffer;
    iph->totalLength+= sizeof(IP);
#endif
    return retval;
}

int
RawSocket::sendTo(char* buffer,
/* [<][>][^][v][top][bottom][index][help] */
                  int bufferLength,
                  Address& addr,
                  Port port) const
{
    struct sockaddr_in sin;
    int retval;

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(addr.get());
    sin.sin_port = htons(port);
    retval = ::sendto(descriptor_,
                      buffer,
                      bufferLength,
                      0,
                      (struct sockaddr *) &sin,
                      sizeof(sin));
    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
            case EINTR:
                return FileOpSoftError;
            default:
                return FileOpHardError;
        }
    }

    TRACE(traceFile,
          "wrote %d bytes on socket %d from %s\n",
          retval,
          descriptor_,
          addr.name());
    
    return retval;
}

int
RawSocket::join(Address& group)
/* [<][>][^][v][top][bottom][index][help] */
{
    struct ip_mreq mreq;

    mreq.imr_multiaddr.s_addr = htonl(group.get());
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    TRACE(traceFile,
          "joining group %s on %d\n",
          group.name(),
          descriptor_);

    if (::setsockopt(descriptor_,
                     IPPROTO_IP,
                     IP_ADD_MEMBERSHIP,
                     (char *) &mreq,
                     sizeof(mreq)) < 0) {
        FATAL("failed group join on %d: %s\n",
              descriptor_,
              strerror(errno));
        // NotReached
    }

    return FileOpOK;
}

MulticastSocket::MulticastSocket(const Address& addr,
/* [<][>][^][v][top][bottom][index][help] */
                                 Port localPort,
                                 const Handler& rh,
                                 const Handler& wh)
        : DatagramSocket(rh, wh)
{
    struct ip_mreq mreq;
    struct sockaddr_in sin;
    char loop;

    group = addr;
    port = localPort;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(localPort);
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    
    TRACE(traceFile,
          "binding socket %d to port %d\n",
          descriptor_,
          localPort);

    if (::bind(descriptor_,
               (struct sockaddr *) &sin,
               sizeof(sin)) < 0) {
        FATAL("failed bind on %d: %s\n",
              descriptor_,
              strerror(errno));
        // NotReached
    }

    mreq.imr_multiaddr.s_addr = htonl(group.get());
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    TRACE(traceFile,
          "joining group %s on %d\n",
          group.name(),
          descriptor_);

    if (::setsockopt(descriptor_,
                     IPPROTO_IP,
                     IP_ADD_MEMBERSHIP,
                     (char *) &mreq,
                     sizeof(mreq)) < 0) {
        FATAL("failed group join on %d: %s\n",
              descriptor_,
              strerror(errno));
        // NotReached
    }

    // Disable loopback of packets
    loop = 0;
    if (::setsockopt(descriptor_,
                     IPPROTO_IP,
                     IP_MULTICAST_LOOP,
                     &loop,
                     sizeof(loop)) < 0) {
        FATAL("unable to disable multicast loopback on %d: %s\n",
              descriptor_,
              strerror(errno));
        // NotReached
    }
}

MulticastSocket::~MulticastSocket()
/* [<][>][^][v][top][bottom][index][help] */
{
    struct ip_mreq mreq;

    mreq.imr_multiaddr.s_addr = htonl(group.get());
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    
    TRACE(traceFile,
          "deleting multicast socket for group %s on %d\n",
          group.name(),
          descriptor_);
    
    (void) ::setsockopt(descriptor_,
                        IPPROTO_IP,
                        IP_DROP_MEMBERSHIP,
                        (char *) &mreq,
                        sizeof(mreq));
    return;
}

int
MulticastSocket::defaultInterface(Address& localAddress)
/* [<][>][^][v][top][bottom][index][help] */
{
    int sock;
    struct sockaddr_in sin;
    size_t socklen = sizeof(sin);

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(group.get());
    sin.sin_port = htons(port);         // We can then use write
    
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        ERROR("couldn't open socket for getting local address: %s\n",
              strerror(errno));
        return FileOpHardError;
    }

    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
        ERROR("couldn't connect for getting local address: %s\n",
              strerror(errno));
        return FileOpHardError;
    }

    if (getsockname(sock, 
                    (struct sockaddr *) &sin, 
                    (SOCKLEN_T*)&socklen) < 0) {
        ERROR("couldn't get local address: %s\n",
              strerror(errno));
        return FileOpHardError;
    }
    
#ifdef HOST_OS_IS_SOLARIS
    if (sin.sin_addr.s_addr == 0) {
        char myhostname[256];
        struct hostent *hp;
        int error;
    
        error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
        if (error == -1) {
            ERROR("failed on sysinfo: %s\n",
                  strerror(errno));
            exit(-1);
        }

        hp = gethostbyname(myhostname);
        if (hp == NULL || hp->h_addrtype != AF_INET ||
            hp->h_length != sizeof(sin.sin_addr)) {
            ERROR("failed on gethostbyname: %s\n",
                  strerror(errno));
            exit(-1);
        }
        memcpy((char *)&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
    }
#endif

    localAddress.set(ntohl(sin.sin_addr.s_addr));
    close(sock);
    TRACE(traceFile,
          "default interface for group %s is %s\n",
          group.name(),
          localAddress.name());
    
    return FileOpOK;
}

int
MulticastSocket::setTTL(int ttl)
/* [<][>][^][v][top][bottom][index][help] */
{
    char t = ttl;

    TRACE(traceFile,
          "setting ttl %d on %d\n",
          ttl,
          descriptor_);
    
    if (::setsockopt(descriptor_,
                     IPPROTO_IP,
                     IP_MULTICAST_TTL,
                     (char *) &t,
                     sizeof(t)) < 0) {
        ERROR("failed ttl set on %d: %s\n",
              descriptor_,
              strerror(errno));
        return FileOpHardError;
    }
    return FileOpOK;
}

StreamSocket::StreamSocket(Address& addr,
/* [<][>][^][v][top][bottom][index][help] */
                           Port port,
                           Handler& rh,
                           Handler& wh)
        : File(::socket(AF_INET, SOCK_STREAM, 0),
               FileModeReadWrite, 
               rh, 
               wh)
{
    struct sockaddr_in sin;
    int retval;

    // Only stream sockets are non-blocking
    if (fcntl(descriptor_,
              F_SETFL,
              O_NDELAY) < 0) {
        FATAL("non-blocking descriptor error: %s\n",
              strerror(errno));
        // Not Reached
    }

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(addr.get());
    sin.sin_port = htons(port);
    
    retval = ::connect(descriptor_,
                       (struct sockaddr *) &sin,
                       sizeof(sin));
    //    ASSERT((retval < 0) && (errno == EWOULDBLOCK));       // XXX
}

StreamSocket::StreamSocket(int d,
/* [<][>][^][v][top][bottom][index][help] */
                           Handler& rh,
                           Handler& wh)
        : File(d, FileModeReadWrite, rh, wh)
{
    // Only stream sockets are non-blocking
    if (fcntl(descriptor_,
              F_SETFL,
              O_NDELAY) < 0) {
        FATAL("non-blocking descriptor error: %s\n",
              strerror(errno));
        // Not Reached
    }
}

StreamSocket::~StreamSocket()
/* [<][>][^][v][top][bottom][index][help] */
{
    // Empty
}

ListenSocket::ListenSocket(Port port,
/* [<][>][^][v][top][bottom][index][help] */
                           Handler& lh,
                           Boolean local)
        : File(::socket(AF_INET, SOCK_STREAM, 0),
               FileModeReadWrite,
               lh,
               nullHandler)
{
    int         retval;
    int         reuse;
    sockaddr_in sin;

    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    if (local) {
        sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    } else {
        sin.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    
    TRACE(traceFile,
          "binding socket %d to port %d\n",
          descriptor_,
          port);

    reuse = 1;
    if (::setsockopt(descriptor_,
                     SOL_SOCKET,
                     SO_REUSEADDR,
                     (char*) &reuse,
                     sizeof(reuse)) < 0) {
        FATAL("failed to set %d to be reusable: %s\n",
              descriptor_,
              strerror(errno));
        // NotReached
    }

    if (::bind(descriptor_,
               (struct sockaddr *) &sin,
               sizeof(sin)) < 0) {
        FATAL("failed bind on %d: %s\n",
              descriptor_,
              strerror(errno));
        // NotReached
    }

    TRACE(traceFile,
          "setting file %d to listen\n",
          descriptor_);

    retval = ::listen(descriptor_, 5);
    if (retval < 0) {
        FATAL("listen failed on file %d\n",
              descriptor_);
        // NotReached
    }
    return;
}

ListenSocket::~ListenSocket()
/* [<][>][^][v][top][bottom][index][help] */
{
    // Empty
}

int
ListenSocket::accept(StreamSocket** sock,
/* [<][>][^][v][top][bottom][index][help] */
                     Address& remote,
                     Port *remotePort,
                     Handler& read,
                     Handler& write)
{
    int retval;
    struct sockaddr_in sin;
    size_t socklen;

    TRACE(traceFile,
          "accepting incoming connection on file %d\n",
          descriptor_);

    socklen = sizeof(sin);
    retval = ::accept(descriptor_,
                      (struct sockaddr *) &sin,
                      (SOCKLEN_T*)&socklen);
    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
            case EINTR:
                return FileOpSoftError;
            default:
                return FileOpHardError;
        }
    }

    remote.set(ntohl(sin.sin_addr.s_addr));
    *remotePort = ntohs(sin.sin_port);

    *sock = new StreamSocket(retval, read, write);
    return FileOpOK;
}

DiskFile::DiskFile(char* fileName,
/* [<][>][^][v][top][bottom][index][help] */
                   Boolean readOnly,
                   Handler& rh,
                   Handler& wh)
        : File(::open(fileName,
                      (readOnly) ? O_RDONLY : (O_CREAT | O_RDWR),
                      0644),
               (readOnly) ? FileModeReadOnly : FileModeReadWrite,
               rh,
               wh)
{
    // Empty
}

DiskFile::~DiskFile()
/* [<][>][^][v][top][bottom][index][help] */
{
    // Empty
}

int
DiskFile::seekTo(u_long offset)
/* [<][>][^][v][top][bottom][index][help] */
{
    int retval;
    
    TRACE(traceFile,
          "seeking to off %u on descriptor %d\n",
          offset,
          descriptor_);
    
    retval = ::lseek(descriptor_, (off_t) offset, SEEK_SET);
    if (retval < 0) {
        return FileOpHardError;
    }
    return retval;
}

int
DiskFile::seekEnd()
/* [<][>][^][v][top][bottom][index][help] */
{
    int retval;
    
    TRACE(traceFile,
          "seeking to end on descriptor %d\n",
          descriptor_);
    
    retval = ::lseek(descriptor_, (off_t) 0, SEEK_END);
    if (retval < 0) {
        return FileOpHardError;
    }
    return retval;
}

int
DiskFile::lock()
/* [<][>][^][v][top][bottom][index][help] */
{
    int retval;

#ifdef HAVE_FLOCK
    retval = ::flock(descriptor_, LOCK_EX|LOCK_NB);
#elif HAVE_LOCKF
    retval = ::lockf(descriptor_, F_LOCK, 0);
#else
    retval = 0;
#endif
    
    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
                TRACE(traceFile,
                      "descriptor %d already locked\n",
                      descriptor_);
                
                return FileOpSoftError;
            default:
                break;
        }
        return FileOpHardError;
    }

    TRACE(traceFile,
          "descriptor %d not locked by another process\n",
          descriptor_);
    
    return FileOpOK;
}

int
DiskFile::truncate(u_int size)
/* [<][>][^][v][top][bottom][index][help] */
{
    int retval;

    retval = ::ftruncate(descriptor_, size);
    if (retval < 0) {
        return FileOpHardError;
    }

    TRACE(traceFile,
          "truncated descriptor %d to size %u\n",
          descriptor_,
          size);
    return FileOpOK;
}    

void
createDir(char* name)
/* [<][>][^][v][top][bottom][index][help] */
{
    int retval;
    struct stat statbuf;

    retval = stat(name, &statbuf);
    if (retval < 0) {   // Name doesn't exist
        retval = mkdir(name, 0755);
        if (retval < 0) {
            FATAL("couldn't create directory %s: %s\n", name,
                  strerror(errno));
            // NotReached
        }
        return;
    }
    if (!S_ISDIR(statbuf.st_mode)) {
        FATAL("expected %s to be a directory, it isn't\n",
              name);
        // NotReached
    }
}

static char*    pidFileName = NULL;

void
openPidFile(const char* name)
/* [<][>][^][v][top][bottom][index][help] */
{
    int         retval;
    char        buf[20];
    int         pid;
    int         len;

    pidFileName = new char[strlen(name) + 1];
    memcpy(pidFileName, name, strlen(name) + 1);

    pidDescriptor = open(name, O_RDWR | O_CREAT | 
#ifdef HOST_OS_IS_FREEBSD
                         O_FSYNC
#else
                         O_SYNC
#endif 
                         , 0664);
    if (pidDescriptor < 0) {
        ERROR("couldn't open pid file %s: %s\n", name, strerror(errno));
        exit(2);
    }

#ifdef HAVE_FLOCK
    retval = ::flock(pidDescriptor, LOCK_EX|LOCK_NB);
#elif HAVE_LOCKF
    retval = ::lockf(pidDescriptor, F_LOCK, 0);
#else
    retval = 0;
#endif

    if (retval < 0) {
        switch (errno) {
            case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
            case EAGAIN:
#endif
                len = read(pidDescriptor, buf, sizeof buf);
                if (len > 0 &&  (pid = atoi(buf))) {
                    ERROR("another already running, pid %d\n",
                          pid);
                } else {
                    ERROR("is some other %s running?");
                }
                break;

            default:
                ERROR("flock failed: %s\n", strerror(errno));
        }
        close(pidDescriptor);
        pidDescriptor = -1;
        exit(2);
    }

    (void) sprintf(buf, "%d\n", getpid());
    len = strlen(buf);

#ifndef SEEK_SET
#define SEEK_SET        L_SET
#endif  /* SEEK_SET */
    /* Back up to the beginning and truncate the file */
    if (lseek(pidDescriptor, (off_t) 0, SEEK_SET) < 0
        || ftruncate(pidDescriptor, (off_t) 0) < 0
        || write(pidDescriptor, buf, len) != len) {
        ERROR("couldn't write to %s: %s\n", name, strerror(errno));
        exit(2);
    }

    return;
}

void
closePidFile()
/* [<][>][^][v][top][bottom][index][help] */
{
    if (!pidFileName) {
        return;
    }

    if (pidDescriptor > -1) {
        if (close(pidDescriptor) == -1
            || unlink(pidFileName) == -1) {
            ERROR("could not close or remove %s: %s\n",
                  pidFileName, strerror(errno));
            exit(2);
        }
        pidDescriptor = -1;
    }

    delete [] pidFileName;
    return;
}

void
daemonize()
/* [<][>][^][v][top][bottom][index][help] */
{
    int         t;
    Signal*     sig;

    // Ignore several signals
    sig = new Signal("ttou", NULL);
    sig = new Signal("ttin", NULL);
    sig = new Signal("tstp", NULL);

    // Fork once
    switch (fork()) {
        case 0:
            break;
        case -1:
            perror("daemonize: fork");
            exit(1);
        default:
            exit(0);
    }

#ifdef SETPGRP_VOID
    t = setpgrp();
    if (t < 0) {
        perror("daemonize: setprgp");
        exit(1);
    }

    sig = new Signal("hup", NULL);

    switch (fork()) {
        case 0:
            break;
        case -1:
            perror("daemonize: fork");
            exit(1);
        default:
            exit(0);
    } 
#else // SETPRGP_VOID
    t = setpgrp(0, getpid());
    if (t < 0) {
        perror("daemonize: setpgrp");
        exit(1);
    }
    
    while ((t = open("/dev/tty", O_RDWR, 0)) == -1 && errno == EINTR);
    if (t >= 0) {
        if (ioctl(t, TIOCNOTTY, (caddr_t) 0) < 0) {
            perror("daemonize: ioctl(TIOCNOTTY)");
            exit(1);
        }
        close(t);
    }
#endif // SETPGRP_VOID

    // Can close all open files, and reset umask
    {
#ifndef NOFILE
#ifdef  _NFILE
#define NOFILE  _NFILE
#else   /* _NFILE */
#ifdef  OPEN_MAX
#define NOFILE  OPEN_MAX
#else   /* OPEN_MAX */
#define NOFILE  20
#endif  /* OPEN_MAX */
#endif  /* _NFILE */
#endif  /* NOFILE */
        t = NOFILE;
        do {
            (void) close(t);
        } while (--t);

    }
            
    /* Reset umask */
    umask(022);
}

//
//  Copyright (c) 1994 by the University of Southern California.
//  All rights reserved.
//
//  Permission to use, copy, modify, and distribute this software and
//  its documentation in source and binary forms for lawful
//  non-commercial purposes and without fee is hereby granted, provided
//  that the above copyright notice appear in all copies and that both
//  the copyright notice and this permission notice appear in supporting
//  documentation, and that any documentation, advertising materials,
//  and other materials related to such distribution and use acknowledge
//  that the software was developed by the University of Southern
//  California and/or Information Sciences Institute.
//  The name of the University of Southern California may not
//  be used to endorse or promote products derived from this software
//  without specific prior written permission.
//
//  THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
//  ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
//  PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND 
//  NON-INFRINGEMENT.
//
//  IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
//  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
//  TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
//  THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
//  Questions concerning this software should be directed to 
//  info-ra@isi.edu.
//

/* [<][>][^][v][top][bottom][index][help] */