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