blob: 2b8a0aa8b4d34b4e53e7dc29e0c4337556e335fd [file] [log] [blame]
#define LOG_TAG "VNCFlinger"
#include <utils/Log.h>
#include <fcntl.h>
#include <fstream>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include "AndroidDesktop.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <network/Socket.h>
#include <network/TcpSocket.h>
#include <rfb/Configuration.h>
#include <rfb/LogWriter.h>
#include <rfb/Logger_android.h>
#include <rfb/VNCServerST.h>
#include <rfb/util.h>
using namespace vncflinger;
using namespace android;
static char* gProgramName;
static bool gCaughtSignal = false;
static std::string mPidFile;
static rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol", 5900);
static void printVersion(FILE* fp) {
fprintf(fp, "VNCFlinger 1.0");
}
static void CleanupSignalHandler(int)
{
ALOGI("You killed me - cleaning up");
if (mPidFile.length() != 0) {
remove(mPidFile.c_str());
}
exit(1);
}
static void usage() {
printVersion(stderr);
fprintf(stderr, "\nUsage: %s [<parameters>]\n", gProgramName);
fprintf(stderr, " %s --version\n", gProgramName);
fprintf(stderr,
"\n"
"Parameters can be turned on with -<param> or off with -<param>=0\n"
"Parameters which take a value can be specified as "
"-<param> <value>\n"
"Other valid forms are <param>=<value> -<param>=<value> "
"--<param>=<value>\n"
"Parameter names are case-insensitive. The parameters are:\n\n");
rfb::Configuration::listParams(79, 14);
exit(1);
}
int main(int argc, char** argv) {
rfb::initAndroidLogger();
rfb::LogWriter::setLogParams("*:android:30");
gProgramName = argv[0];
rfb::Configuration::enableServerParams();
#ifdef SIGHUP
signal(SIGHUP, CleanupSignalHandler);
#endif
signal(SIGINT, CleanupSignalHandler);
signal(SIGTERM, CleanupSignalHandler);
for (int i = 1; i < argc; i++) {
if (rfb::Configuration::setParam(argv[i])) continue;
if (argv[i][0] == '-') {
if (i + 1 < argc) {
if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
i++;
continue;
}
}
if (strcmp(argv[i], "-pid") == 0) {
if (i + 1 < argc) {
mPidFile = std::string(argv[i + 1]);
i++;
continue;
}
}
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "-version") == 0 ||
strcmp(argv[i], "--version") == 0) {
printVersion(stdout);
return 0;
}
usage();
}
usage();
}
sp<ProcessState> self = ProcessState::self();
self->startThreadPool();
std::list<network::SocketListener*> listeners;
int ret = 0;
try {
sp<AndroidDesktop> desktop = new AndroidDesktop();
rfb::VNCServerST server("vncflinger", desktop.get());
network::createTcpListeners(&listeners, 0, (int)rfbport);
int eventFd = desktop->getEventFd();
fcntl(eventFd, F_SETFL, O_NONBLOCK);
ALOGI("Listening on port %d", (int)rfbport);
if (mPidFile.length() != 0) {
// write a pid file
ALOGI("pid file %s", mPidFile.c_str());
pid_t pid = getpid();
std::ofstream outfile(mPidFile);
outfile << pid;
outfile.close();
}
while (!gCaughtSignal) {
int wait_ms;
struct timeval tv;
fd_set rfds, wfds;
std::list<network::Socket*> sockets;
std::list<network::Socket*>::iterator i;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(eventFd, &rfds);
for (std::list<network::SocketListener*>::iterator i = listeners.begin();
i != listeners.end(); i++)
FD_SET((*i)->getFd(), &rfds);
server.getSockets(&sockets);
int clients_connected = 0;
for (i = sockets.begin(); i != sockets.end(); i++) {
if ((*i)->isShutdown()) {
server.removeSocket(*i);
delete (*i);
} else {
FD_SET((*i)->getFd(), &rfds);
if ((*i)->outStream().bufferUsage() > 0) FD_SET((*i)->getFd(), &wfds);
clients_connected++;
}
}
wait_ms = 0;
rfb::soonestTimeout(&wait_ms, rfb::Timer::checkTimeouts());
tv.tv_sec = wait_ms / 1000;
tv.tv_usec = (wait_ms % 1000) * 1000;
int n = select(FD_SETSIZE, &rfds, &wfds, 0, wait_ms ? &tv : NULL);
if (n < 0) {
if (errno == EINTR) {
ALOGV("Interrupted select() system call");
continue;
} else {
throw rdr::SystemException("select", errno);
}
}
// Accept new VNC connections
for (std::list<network::SocketListener*>::iterator i = listeners.begin();
i != listeners.end(); i++) {
if (FD_ISSET((*i)->getFd(), &rfds)) {
network::Socket* sock = (*i)->accept();
if (sock) {
sock->outStream().setBlocking(false);
server.addSocket(sock);
} else {
ALOGW("Client connection rejected");
}
}
}
rfb::Timer::checkTimeouts();
// Client list could have been changed.
server.getSockets(&sockets);
// Nothing more to do if there are no client connections.
if (sockets.empty()) continue;
// Process events on existing VNC connections
for (i = sockets.begin(); i != sockets.end(); i++) {
if (FD_ISSET((*i)->getFd(), &rfds)) server.processSocketReadEvent(*i);
if (FD_ISSET((*i)->getFd(), &wfds)) server.processSocketWriteEvent(*i);
}
// Process events from the display
uint64_t eventVal;
int status = read(eventFd, &eventVal, sizeof(eventVal));
if (status > 0 && eventVal > 0) {
//ALOGV("status=%d eventval=%lu", status, eventVal);
desktop->processFrames();
}
}
ret = 0;
} catch (rdr::Exception& e) {
ALOGE("%s", e.str());
ret = 1;
}
ALOGI("Bye - cleaning up");
if (mPidFile.length() != 0) {
remove(mPidFile.c_str());
}
return ret;
}