tools: Fixed printf % code mismatches
[barry.git] / tools / brawchannel_unix.cc
blobd34658a725afb99d3d85f6c2514684785ba22ad1
1 ///
2 /// \file brawchannel_unix.cc
3 /// Implements OS support for STDIN/STDOUT and TCP
4 ///
6 /*
7 Copyright (C) 2010-2012, RealVNC Ltd.
9 Some parts are inspired from bjavaloader.cc
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "brawchannel.h"
25 #include "i18n.h"
26 #include <barry/barry.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <iostream>
31 #include <netdb.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37 #include <arpa/inet.h>
38 #include <fcntl.h>
40 #define SD_SEND SHUT_WR
41 #define INVALID_SOCKET -1
42 #define INVALID_HANDLE -1
44 using namespace std;
45 using namespace Barry;
47 struct TcpStreamImpl
49 in_addr mHostAddress;
50 // FIXME - this should be a std::string if ever moved to the library
51 const char * mListenAddress;
52 long mPort;
53 int mListenSocket;
54 int mSocket;
55 int mLastError;
59 ssize_t StdOutStream::write(const unsigned char* ptr, size_t size)
61 size_t written = fwrite(ptr, 1, size, stdout);
62 if( written == 0 &&
63 ( ferror(stdout) != 0 || feof(stdout) != 0 ) ) {
64 return -1;
66 return static_cast<ssize_t>(written);
69 ssize_t StdInStream::read(unsigned char* ptr, size_t size, int timeout)
71 fd_set rfds;
72 struct timeval tv;
73 FD_ZERO(&rfds);
74 FD_SET(STDIN_FILENO, &rfds);
75 tv.tv_sec = READ_TIMEOUT_SECONDS;
76 tv.tv_usec = 0;
77 int ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv);
78 if( ret < 0 ) {
79 cerr << _("Select failed with errno: ") << errno << endl;
80 return -1;
81 } else if ( ret && FD_ISSET(STDIN_FILENO, &rfds) ) {
82 return ::read(STDIN_FILENO, ptr, size);
83 } else {
84 return 0;
88 TcpStream::TcpStream(const char * addr, long port)
90 mImpl.reset(new TcpStreamImpl);
91 mImpl->mListenAddress = addr;
92 mImpl->mPort = port;
93 mImpl->mSocket = INVALID_SOCKET;
94 mImpl->mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
95 if( mImpl->mListenSocket == INVALID_SOCKET ) {
96 cerr << _("Failed to create listening socket: ") <<
97 errno << endl;
99 if( mImpl->mListenAddress == NULL ) {
100 mImpl->mHostAddress.s_addr = INADDR_ANY;
101 } else {
102 mImpl->mHostAddress.s_addr = inet_addr(mImpl->mListenAddress);
106 TcpStream::~TcpStream()
108 if( mImpl->mSocket != INVALID_SOCKET ) {
109 shutdown(mImpl->mSocket, SD_SEND);
110 close(mImpl->mSocket);
111 mImpl->mSocket = INVALID_SOCKET;
113 if( mImpl->mListenSocket != INVALID_SOCKET ) {
114 shutdown(mImpl->mListenSocket, SD_SEND);
115 close(mImpl->mListenSocket);
116 mImpl->mListenSocket = INVALID_SOCKET;
120 bool TcpStream::accept()
122 if( mImpl->mListenSocket == INVALID_SOCKET ||
123 mImpl->mLastError != 0 ||
124 mImpl->mHostAddress.s_addr == INADDR_NONE ) {
125 return false;
127 struct sockaddr_in serverAddr;
128 memset(&serverAddr, 0, sizeof(serverAddr));
129 serverAddr.sin_family = AF_INET;
130 serverAddr.sin_addr = mImpl->mHostAddress;
131 serverAddr.sin_port = htons(static_cast<u_short>(mImpl->mPort));
132 if( ::bind(mImpl->mListenSocket, (sockaddr*) & serverAddr, sizeof(serverAddr)) < 0 ) {
133 cerr << _("Failed to bind to listening address") << endl;
134 return false;
137 // Set the socket options
138 int one = 1;
139 if( setsockopt(mImpl->mListenSocket, SOL_SOCKET, SO_REUSEADDR,
140 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
141 cerr << _("Failed to enable reuse of address") << endl;
142 return false;
145 if( fcntl(mImpl->mListenSocket, F_SETFL,
146 fcntl(mImpl->mListenSocket, F_GETFL, 0) | O_NONBLOCK) < 0 ) {
147 cerr << _("Failed to set non-blocking listening socket") << endl;
148 return false;
151 if( ::listen(mImpl->mListenSocket, 5) == INVALID_SOCKET ) {
152 cerr << _("Failed to listen to listening address") << endl;
153 return false;
156 struct sockaddr_in clientAddr;
157 socklen_t len = sizeof(clientAddr);
158 cout << string_vprintf(_("Listening for connection on %s:%ld"),
159 ( mImpl->mListenAddress == NULL ? "*" : mImpl->mListenAddress ),
160 mImpl->mPort) << endl;
162 mImpl->mSocket = ::accept(mImpl->mListenSocket, (struct sockaddr*) &clientAddr, &len);
163 shutdown(mImpl->mListenSocket, SD_SEND);
164 close(mImpl->mListenSocket);
165 mImpl->mListenSocket = INVALID_SOCKET;
166 if( mImpl->mSocket == INVALID_SOCKET ) {
167 cerr << _("Failed to accept on listening socket") << endl;
168 return false;
171 if( setsockopt(mImpl->mSocket, IPPROTO_TCP, TCP_NODELAY,
172 reinterpret_cast<const char *> (&one), sizeof(one)) < 0 ) {
173 cerr << _("Failed to set no delay") << endl;
174 return false;
177 return true;
180 ssize_t TcpInStream::read(unsigned char* ptr, size_t size, int timeout)
182 fd_set rfds;
183 struct timeval tv;
184 FD_ZERO(&rfds);
185 FD_SET(mStream.mImpl->mSocket, &rfds);
186 tv.tv_sec = READ_TIMEOUT_SECONDS;
187 tv.tv_usec = 0;
188 int ret = select(mStream.mImpl->mSocket + 1, &rfds, NULL, NULL, &tv);
189 if( ret < 0 ) {
190 cerr << _("Select failed with errno: ") << errno << endl;
191 return -1;
192 } else if ( ret && FD_ISSET(mStream.mImpl->mSocket, &rfds) ) {
193 return ::recv(mStream.mImpl->mSocket, reinterpret_cast<char *>(ptr), size, 0);
194 } else {
195 return 0;
199 ssize_t TcpOutStream::write(const unsigned char* ptr, size_t size)
201 return ::send(mStream.mImpl->mSocket, reinterpret_cast<const char*>(ptr), size, 0);