WvStreams
wvunixdgsocket.cc
00001 #include "wvunixdgsocket.h"
00002 #ifdef MACOS
00003 #include <sys/types.h>
00004 #include <sys/stat.h>
00005 #endif
00006 
00007 WvUnixDGSocket::WvUnixDGSocket(WvStringParm filename, bool _server, int perms)
00008     : socketfile(filename)
00009 {
00010 //    log(WvLog::Debug2, "Starting up %s!\n", filename);
00011     server = _server;
00012     backoff = 10;
00013 
00014     bufsize = 0;
00015 
00016     // open a datagram unix domain socket
00017     setfd(socket(PF_UNIX, SOCK_DGRAM, 0));
00018 
00019     // if we don't have a file desciptor, something is wrong.
00020     if (getfd() < 0)
00021     {
00022         seterr("No Socket available.");
00023         return;
00024     }
00025 
00026     // set non-blocking mode
00027     fcntl(getfd(), F_SETFL, O_RDWR|O_NONBLOCK);
00028 
00029     WvUnixAddr uaddr(socketfile);
00030 
00031     // Let this file be reusable, since we're going to own this anyway
00032     // The business with the int x is just Unix stupidities.. *sigh*
00033     int x = 1;
00034     setsockopt(getfd(), SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00035 
00036     if (server)
00037     {
00038         // Fix it so that there can't be another process on this file
00039         unlink(socketfile);
00040 
00041         // Actually bind to the address we set up above.
00042         sockaddr *addr = uaddr.sockaddr();
00043         if (bind(getfd(), (sockaddr *)addr, uaddr.sockaddr_len()))
00044         {
00045             seterr("Bind to %s failed: %s", socketfile, strerror(errno));
00046             close();
00047         }
00048         delete addr;
00049 
00050         chmod(socketfile, perms);
00051     }
00052     else
00053     {
00054         // we're the client, so we connect to someone else's socket
00055         sockaddr *addr = uaddr.sockaddr();
00056         if (connect(getfd(), (sockaddr *)addr, uaddr.sockaddr_len()))
00057         {
00058             seterr("Connect to %s failed: %s",
00059                    socketfile, strerror(errno));
00060             close();
00061         }
00062         delete addr;
00063     }
00064 
00065     drain();
00066 }
00067 
00068 WvUnixDGSocket::~WvUnixDGSocket()
00069 {
00070 //    log(WvLog::Debug2, "Destroying: %s\n", socketfile);
00071     close();
00072     if (server)
00073         unlink(socketfile);
00074 }
00075 
00076 size_t WvUnixDGSocket::uwrite(const void *buf, size_t count)
00077 {
00078     size_t ret = bufs.isempty() ? WvFDStream::uwrite(buf, count) : 0;
00079 
00080     if (ret < count)
00081     {
00082         WvDynBuf *b = new WvDynBuf;
00083         b->put(buf, count);
00084         bufs.append(b, true);
00085         bufsize += count;
00086     }
00087 
00088     return count;
00089 }
00090 
00091 void WvUnixDGSocket::pre_select(SelectInfo &si)
00092 {
00093     SelectRequest oldwant = si.wants;
00094     if (!bufs.isempty())
00095     {
00096         // stupid unix domain sockets seem to return true when selecting
00097         // for write EVEN IF write() RETURNS -EAGAIN!  Just shoot me.
00098         // 
00099         // To deal with this, we set an alarm() in post_select() if we
00100         // couldn't write everything we wanted.  While the alarm is set,
00101         // we don't try to flush our output buffer.
00102         if (alarm_remaining() <= 0)
00103             si.wants.writable = true;
00104         else if (si.msec_timeout < 0
00105                  || si.msec_timeout > alarm_remaining())
00106             si.msec_timeout = alarm_remaining();
00107     }
00108 
00109     WvFDStream::pre_select(si);
00110 
00111     si.wants = oldwant;
00112 }
00113 
00114 bool WvUnixDGSocket::post_select(SelectInfo &si)
00115 {
00116     SelectRequest oldwant = si.wants;
00117     if (!bufs.isempty())
00118         si.wants.writable = true;
00119 
00120     bool sure = WvFDStream::post_select(si);
00121 
00122     si.wants = oldwant;
00123 
00124     if (sure)
00125     {
00126         // try flushing previous bufs
00127         WvBufList::Iter i(bufs);
00128         for (i.rewind(); i.next(); )
00129         {
00130             int used = i->used();
00131             int retval = WvFDStream::uwrite(i->get(used), used);
00132             if (retval < used)
00133             {
00134                 i->unget(used);
00135                 alarm(backoff *= 2);
00136                 if (backoff > 1000)
00137                     backoff = 1000;
00138                 break; // can't continue
00139             }
00140             else
00141             {
00142                 bufsize -= used;
00143                 i.xunlink(); // done with that one
00144                 backoff = 10;
00145             }
00146         }
00147     }
00148 
00149     return sure;
00150 }
00151 
00152