diff options
| author | 2018-04-25 18:07:30 -0400 | |
|---|---|---|
| committer | 2018-04-25 18:07:30 -0400 | |
| commit | 9b1b081cfdb1c0fb6457278775e0823f8bc10f62 (patch) | |
| tree | ce8840148d8445055ba9e4f12263b2208f234c16 /src/lib/arch/unix | |
Import Upstream version 2.0.0+dfsgupstream/2.0.0+dfsg
Diffstat (limited to 'src/lib/arch/unix')
26 files changed, 3298 insertions, 0 deletions
diff --git a/src/lib/arch/unix/ArchConsoleUnix.cpp b/src/lib/arch/unix/ArchConsoleUnix.cpp new file mode 100644 index 0000000..79a4634 --- /dev/null +++ b/src/lib/arch/unix/ArchConsoleUnix.cpp @@ -0,0 +1,23 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchConsoleUnix.h" + +ArchConsoleUnix::ArchConsoleUnix() { } + +ArchConsoleUnix::~ArchConsoleUnix() { } diff --git a/src/lib/arch/unix/ArchConsoleUnix.h b/src/lib/arch/unix/ArchConsoleUnix.h new file mode 100644 index 0000000..8326ab5 --- /dev/null +++ b/src/lib/arch/unix/ArchConsoleUnix.h @@ -0,0 +1,29 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/ArchConsoleStd.h" + +#define ARCH_CONSOLE ArchConsoleUnix + +class ArchConsoleUnix : public ArchConsoleStd { +public: + ArchConsoleUnix(); + virtual ~ArchConsoleUnix(); +}; diff --git a/src/lib/arch/unix/ArchDaemonUnix.cpp b/src/lib/arch/unix/ArchDaemonUnix.cpp new file mode 100644 index 0000000..a03bf7a --- /dev/null +++ b/src/lib/arch/unix/ArchDaemonUnix.cpp @@ -0,0 +1,132 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchDaemonUnix.h" + +#include "arch/unix/XArchUnix.h" +#include "base/Log.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <cstdlib> + +// +// ArchDaemonUnix +// + +ArchDaemonUnix::ArchDaemonUnix() +{ + // do nothing +} + +ArchDaemonUnix::~ArchDaemonUnix() +{ + // do nothing +} + + +#ifdef __APPLE__ + +// In Mac OS X, fork()'d child processes can't use most APIs (the frameworks +// that Barrier uses in fact prevent it and make the process just up and die), +// so need to exec a copy of the program that doesn't fork so isn't limited. +int +execSelfNonDaemonized() +{ + extern char** NXArgv; + char** selfArgv = NXArgv; + + setenv("_BARRIER_DAEMONIZED", "", 1); + + execvp(selfArgv[0], selfArgv); + return 0; +} + +bool alreadyDaemonized() { + return getenv("_BARRIER_DAEMONIZED") != NULL; +} + +#endif + +int +ArchDaemonUnix::daemonize(const char* name, DaemonFunc func) +{ +#ifdef __APPLE__ + if (alreadyDaemonized()) + return func(1, &name); +#endif + + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + throw XArchDaemonFailed(new XArchEvalUnix(errno)); + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + +#ifndef __APPLE__ + // NB: don't run chdir on apple; causes strange behaviour. + // chdir to root so we don't keep mounted filesystems points busy + // TODO: this is a bit of a hack - can we find a better solution? + int chdirErr = chdir("/"); + if (chdirErr) + // NB: file logging actually isn't working at this point! + LOG((CLOG_ERR "chdir error: %i", chdirErr)); +#endif + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + + int dupErr = dup(1); + + if (dupErr < 0) { + // NB: file logging actually isn't working at this point! + LOG((CLOG_ERR "dup error: %i", dupErr)); + } + +#ifdef __APPLE__ + return execSelfNonDaemonized(); +#endif + + // invoke function + return func(1, &name); +} diff --git a/src/lib/arch/unix/ArchDaemonUnix.h b/src/lib/arch/unix/ArchDaemonUnix.h new file mode 100644 index 0000000..530159a --- /dev/null +++ b/src/lib/arch/unix/ArchDaemonUnix.h @@ -0,0 +1,36 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/ArchDaemonNone.h" + +#undef ARCH_DAEMON +#define ARCH_DAEMON ArchDaemonUnix + +//! Unix implementation of IArchDaemon +class ArchDaemonUnix : public ArchDaemonNone { +public: + ArchDaemonUnix(); + virtual ~ArchDaemonUnix(); + + // IArchDaemon overrides + virtual int daemonize(const char* name, DaemonFunc func); +}; + +#define CONFIG_FILE "/etc/barrier/barrierd.conf" diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp new file mode 100644 index 0000000..d9ae8b2 --- /dev/null +++ b/src/lib/arch/unix/ArchFileUnix.cpp @@ -0,0 +1,163 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchFileUnix.h" + +#include <stdio.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#include <cstring> + +// +// ArchFileUnix +// + +ArchFileUnix::ArchFileUnix() +{ + // do nothing +} + +ArchFileUnix::~ArchFileUnix() +{ + // do nothing +} + +const char* +ArchFileUnix::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + return basename + 1; + } + else { + return pathname; + } +} + +std::string +ArchFileUnix::getUserDirectory() +{ + char* buffer = NULL; + std::string dir; +#if HAVE_GETPWUID_R + struct passwd pwent; + struct passwd* pwentp; +#if defined(_SC_GETPW_R_SIZE_MAX) + long size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size == -1) { + size = BUFSIZ; + } +#else + long size = BUFSIZ; +#endif + buffer = new char[size]; + getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); +#else + struct passwd* pwentp = getpwuid(getuid()); +#endif + if (pwentp != NULL && pwentp->pw_dir != NULL) { + dir = pwentp->pw_dir; + } + delete[] buffer; + return dir; +} + +std::string +ArchFileUnix::getSystemDirectory() +{ + return "/etc"; +} + +std::string +ArchFileUnix::getInstalledDirectory() +{ +#if WINAPI_XWINDOWS + return "/usr/bin"; +#else + return "/Applications/Barrier.app/Contents/MacOS"; +#endif +} + +std::string +ArchFileUnix::getLogDirectory() +{ + return "/var/log"; +} + +std::string +ArchFileUnix::getPluginDirectory() +{ + if (!m_pluginDirectory.empty()) { + return m_pluginDirectory; + } + +#if WINAPI_XWINDOWS + return getProfileDirectory().append("/plugins"); +#else + return getProfileDirectory().append("/Plugins"); +#endif +} + +std::string +ArchFileUnix::getProfileDirectory() +{ + String dir; + if (!m_profileDirectory.empty()) { + dir = m_profileDirectory; + } + else { +#if WINAPI_XWINDOWS + dir = getUserDirectory().append("/.barrier"); +#else + dir = getUserDirectory().append("/Library/Application Support/Barrier"); +#endif + } + return dir; + +} + +std::string +ArchFileUnix::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || path[path.size() - 1] != '/') { + path += '/'; + } + path += suffix; + return path; +} + +void +ArchFileUnix::setProfileDirectory(const String& s) +{ + m_profileDirectory = s; +} + +void +ArchFileUnix::setPluginDirectory(const String& s) +{ + m_pluginDirectory = s; +} diff --git a/src/lib/arch/unix/ArchFileUnix.h b/src/lib/arch/unix/ArchFileUnix.h new file mode 100644 index 0000000..86dea0c --- /dev/null +++ b/src/lib/arch/unix/ArchFileUnix.h @@ -0,0 +1,47 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchFile.h" + +#define ARCH_FILE ArchFileUnix + +//! Unix implementation of IArchFile +class ArchFileUnix : public IArchFile { +public: + ArchFileUnix(); + virtual ~ArchFileUnix(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string getInstalledDirectory(); + virtual std::string getLogDirectory(); + virtual std::string getPluginDirectory(); + virtual std::string getProfileDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); + virtual void setProfileDirectory(const String& s); + virtual void setPluginDirectory(const String& s); + +private: + String m_profileDirectory; + String m_pluginDirectory; +}; diff --git a/src/lib/arch/unix/ArchInternetUnix.cpp b/src/lib/arch/unix/ArchInternetUnix.cpp new file mode 100644 index 0000000..fd1e135 --- /dev/null +++ b/src/lib/arch/unix/ArchInternetUnix.cpp @@ -0,0 +1,126 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2014-2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchInternetUnix.h" + +#include "arch/XArch.h" +#include "common/Version.h" +#include "base/Log.h" + +#include <sstream> +#include <curl/curl.h> + +class CurlFacade { +public: + CurlFacade(); + ~CurlFacade(); + String get(const String& url); + String urlEncode(const String& url); + +private: + CURL* m_curl; +}; + +// +// ArchInternetUnix +// + +String +ArchInternetUnix::get(const String& url) +{ + CurlFacade curl; + return curl.get(url); +} + +String +ArchInternetUnix::urlEncode(const String& url) +{ + CurlFacade curl; + return curl.urlEncode(url); +} + +// +// CurlFacade +// + +static size_t +curlWriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} + +CurlFacade::CurlFacade() : + m_curl(NULL) +{ + CURLcode init = curl_global_init(CURL_GLOBAL_ALL); + if (init != CURLE_OK) { + throw XArch("CURL global init failed."); + } + + m_curl = curl_easy_init(); + if (m_curl == NULL) { + throw XArch("CURL easy init failed."); + } +} + +CurlFacade::~CurlFacade() +{ + if (m_curl != NULL) { + curl_easy_cleanup(m_curl); + } + + curl_global_cleanup(); +} + +String +CurlFacade::get(const String& url) +{ + curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curlWriteCallback); + + std::stringstream userAgent; + userAgent << "Barrier "; + userAgent << kVersion; + curl_easy_setopt(m_curl, CURLOPT_USERAGENT, userAgent.str().c_str()); + + std::string result; + curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &result); + + CURLcode code = curl_easy_perform(m_curl); + if (code != CURLE_OK) { + LOG((CLOG_ERR "curl perform error: %s", curl_easy_strerror(code))); + throw XArch("CURL perform failed."); + } + + return result; +} + +String +CurlFacade::urlEncode(const String& url) +{ + char* resultCStr = curl_easy_escape(m_curl, url.c_str(), 0); + + if (resultCStr == NULL) { + throw XArch("CURL escape failed."); + } + + std::string result(resultCStr); + curl_free(resultCStr); + + return result; +} diff --git a/src/lib/arch/unix/ArchInternetUnix.h b/src/lib/arch/unix/ArchInternetUnix.h new file mode 100644 index 0000000..2413d8f --- /dev/null +++ b/src/lib/arch/unix/ArchInternetUnix.h @@ -0,0 +1,28 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2014-2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#define ARCH_INTERNET ArchInternetUnix + +#include "base/String.h" + +class ArchInternetUnix { +public: + String get(const String& url); + String urlEncode(const String& url); +}; diff --git a/src/lib/arch/unix/ArchLogUnix.cpp b/src/lib/arch/unix/ArchLogUnix.cpp new file mode 100644 index 0000000..b1f9089 --- /dev/null +++ b/src/lib/arch/unix/ArchLogUnix.cpp @@ -0,0 +1,84 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchLogUnix.h" + +#include <syslog.h> + +// +// ArchLogUnix +// + +ArchLogUnix::ArchLogUnix() +{ + // do nothing +} + +ArchLogUnix::~ArchLogUnix() +{ + // do nothing +} + +void +ArchLogUnix::openLog(const char* name) +{ + openlog(name, 0, LOG_DAEMON); +} + +void +ArchLogUnix::closeLog() +{ + closelog(); +} + +void +ArchLogUnix::showLog(bool) +{ + // do nothing +} + +void +ArchLogUnix::writeLog(ELevel level, const char* msg) +{ + // convert level + int priority; + switch (level) { + case kERROR: + priority = LOG_ERR; + break; + + case kWARNING: + priority = LOG_WARNING; + break; + + case kNOTE: + priority = LOG_NOTICE; + break; + + case kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} diff --git a/src/lib/arch/unix/ArchLogUnix.h b/src/lib/arch/unix/ArchLogUnix.h new file mode 100644 index 0000000..cdd733f --- /dev/null +++ b/src/lib/arch/unix/ArchLogUnix.h @@ -0,0 +1,36 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchLog.h" + +#define ARCH_LOG ArchLogUnix + +//! Unix implementation of IArchLog +class ArchLogUnix : public IArchLog { +public: + ArchLogUnix(); + virtual ~ArchLogUnix(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void showLog(bool); + virtual void writeLog(ELevel, const char*); +}; diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp new file mode 100644 index 0000000..ade6c51 --- /dev/null +++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp @@ -0,0 +1,812 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchMultithreadPosix.h" + +#include "arch/Arch.h" +#include "arch/XArch.h" + +#include <signal.h> +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#include <cerrno> + +#define SIGWAKEUP SIGUSR1 + +#if !HAVE_PTHREAD_SIGNAL + // boy, is this platform broken. forget about pthread signal + // handling and let signals through to every process. barrier + // will not terminate cleanly when it gets SIGTERM or SIGINT. +# define pthread_sigmask sigprocmask +# define pthread_kill(tid_, sig_) kill(0, (sig_)) +# define sigwait(set_, sig_) +# undef HAVE_POSIX_SIGWAIT +# define HAVE_POSIX_SIGWAIT 1 +#endif + +static +void +setSignalSet(sigset_t* sigset) +{ + sigemptyset(sigset); + sigaddset(sigset, SIGHUP); + sigaddset(sigset, SIGINT); + sigaddset(sigset, SIGTERM); + sigaddset(sigset, SIGUSR2); +} + +// +// ArchThreadImpl +// + +class ArchThreadImpl { +public: + ArchThreadImpl(); + +public: + int m_refCount; + IArchMultithread::ThreadID m_id; + pthread_t m_thread; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + bool m_cancel; + bool m_cancelling; + bool m_exited; + void* m_result; + void* m_networkData; +}; + +ArchThreadImpl::ArchThreadImpl() : + m_refCount(1), + m_id(0), + m_func(NULL), + m_userData(NULL), + m_cancel(false), + m_cancelling(false), + m_exited(false), + m_result(NULL), + m_networkData(NULL) +{ + // do nothing +} + + +// +// ArchMultithreadPosix +// + +ArchMultithreadPosix* ArchMultithreadPosix::s_instance = NULL; + +ArchMultithreadPosix::ArchMultithreadPosix() : + m_newThreadCalled(false), + m_nextID(0) +{ + assert(s_instance == NULL); + + s_instance = this; + + // no signal handlers + for (size_t i = 0; i < kNUM_SIGNALS; ++i) { + m_signalFunc[i] = NULL; + m_signalUserData[i] = NULL; + } + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + m_mainThread = new ArchThreadImpl; + m_mainThread->m_thread = pthread_self(); + insert(m_mainThread); + + // install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt + // system calls. we use that when cancelling a thread to force it + // to wake up immediately if it's blocked in a system call. we + // won't need this until another thread is created but it's fine + // to install it now. + struct sigaction act; + sigemptyset(&act.sa_mask); +# if defined(SA_INTERRUPT) + act.sa_flags = SA_INTERRUPT; +# else + act.sa_flags = 0; +# endif + act.sa_handler = &threadCancel; + sigaction(SIGWAKEUP, &act, NULL); + + // set desired signal dispositions. let SIGWAKEUP through but + // ignore SIGPIPE (we'll handle EPIPE). + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGWAKEUP); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); +} + +ArchMultithreadPosix::~ArchMultithreadPosix() +{ + assert(s_instance != NULL); + + closeMutex(m_threadMutex); + s_instance = NULL; +} + +void +ArchMultithreadPosix::setNetworkDataForCurrentThread(void* data) +{ + lockMutex(m_threadMutex); + ArchThreadImpl* thread = find(pthread_self()); + thread->m_networkData = data; + unlockMutex(m_threadMutex); +} + +void* +ArchMultithreadPosix::getNetworkDataForThread(ArchThread thread) +{ + lockMutex(m_threadMutex); + void* data = thread->m_networkData; + unlockMutex(m_threadMutex); + return data; +} + +ArchMultithreadPosix* +ArchMultithreadPosix::getInstance() +{ + return s_instance; +} + +ArchCond +ArchMultithreadPosix::newCondVar() +{ + ArchCondImpl* cond = new ArchCondImpl; + int status = pthread_cond_init(&cond->m_cond, NULL); + (void)status; + assert(status == 0); + return cond; +} + +void +ArchMultithreadPosix::closeCondVar(ArchCond cond) +{ + int status = pthread_cond_destroy(&cond->m_cond); + (void)status; + assert(status == 0); + delete cond; +} + +void +ArchMultithreadPosix::signalCondVar(ArchCond cond) +{ + int status = pthread_cond_signal(&cond->m_cond); + (void)status; + assert(status == 0); +} + +void +ArchMultithreadPosix::broadcastCondVar(ArchCond cond) +{ + int status = pthread_cond_broadcast(&cond->m_cond); + (void)status; + assert(status == 0); +} + +bool +ArchMultithreadPosix::waitCondVar(ArchCond cond, + ArchMutex mutex, double timeout) +{ + // we can't wait on a condition variable and also wake it up for + // cancellation since we don't use posix cancellation. so we + // must wake up periodically to check for cancellation. we + // can't simply go back to waiting after the check since the + // condition may have changed and we'll have lost the signal. + // so we have to return to the caller. since the caller will + // always check for spurious wakeups the only drawback here is + // performance: we're waking up a lot more than desired. + static const double maxCancellationLatency = 0.1; + if (timeout < 0.0 || timeout > maxCancellationLatency) { + timeout = maxCancellationLatency; + } + + // see if we should cancel this thread + testCancelThread(); + + // get final time + struct timeval now; + gettimeofday(&now, NULL); + struct timespec finalTime; + finalTime.tv_sec = now.tv_sec; + finalTime.tv_nsec = now.tv_usec * 1000; + long timeout_sec = (long)timeout; + long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec)); + finalTime.tv_sec += timeout_sec; + finalTime.tv_nsec += timeout_nsec; + if (finalTime.tv_nsec >= 1000000000) { + finalTime.tv_nsec -= 1000000000; + finalTime.tv_sec += 1; + } + + // wait + int status = pthread_cond_timedwait(&cond->m_cond, + &mutex->m_mutex, &finalTime); + + // check for cancel again + testCancelThread(); + + switch (status) { + case 0: + // success + return true; + + case ETIMEDOUT: + return false; + + default: + assert(0 && "condition variable wait error"); + return false; + } +} + +ArchMutex +ArchMultithreadPosix::newMutex() +{ + pthread_mutexattr_t attr; + int status = pthread_mutexattr_init(&attr); + assert(status == 0); + ArchMutexImpl* mutex = new ArchMutexImpl; + status = pthread_mutex_init(&mutex->m_mutex, &attr); + assert(status == 0); + return mutex; +} + +void +ArchMultithreadPosix::closeMutex(ArchMutex mutex) +{ + int status = pthread_mutex_destroy(&mutex->m_mutex); + (void)status; + assert(status == 0); + delete mutex; +} + +void +ArchMultithreadPosix::lockMutex(ArchMutex mutex) +{ + int status = pthread_mutex_lock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EDEADLK: + assert(0 && "lock already owned"); + break; + + case EAGAIN: + assert(0 && "too many recursive locks"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +void +ArchMultithreadPosix::unlockMutex(ArchMutex mutex) +{ + int status = pthread_mutex_unlock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EPERM: + assert(0 && "thread doesn't own a lock"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +ArchThread +ArchMultithreadPosix::newThread(ThreadFunc func, void* data) +{ + assert(func != NULL); + + // initialize signal handler. we do this here instead of the + // constructor so we can avoid daemonizing (using fork()) + // when there are multiple threads. clients can safely + // use condition variables and mutexes before creating a + // new thread and they can safely use the only thread + // they have access to, the main thread, so they really + // can't tell the difference. + if (!m_newThreadCalled) { + m_newThreadCalled = true; +#if HAVE_PTHREAD_SIGNAL + startSignalHandler(); +#endif + } + + lockMutex(m_threadMutex); + + // create thread impl for new thread + ArchThreadImpl* thread = new ArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // create the thread. pthread_create() on RedHat 7.2 smp fails + // if passed a NULL attr so use a default attr. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&thread->m_thread, &attr, + &ArchMultithreadPosix::threadFunc, thread); + pthread_attr_destroy(&attr); + } + + // check if thread was started + if (status != 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +ArchThread +ArchMultithreadPosix::newCurrentThread() +{ + lockMutex(m_threadMutex); + ArchThreadImpl* thread = find(pthread_self()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +ArchMultithreadPosix::closeThread(ArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // detach from thread (unless it's the main thread) + if (thread->m_func != NULL) { + pthread_detach(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRef(thread->m_thread) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +ArchThread +ArchMultithreadPosix::copyThread(ArchThread thread) +{ + refThread(thread); + return thread; +} + +void +ArchMultithreadPosix::cancelThread(ArchThread thread) +{ + assert(thread != NULL); + + // set cancel and wakeup flags if thread can be cancelled + bool wakeup = false; + lockMutex(m_threadMutex); + if (!thread->m_exited && !thread->m_cancelling) { + thread->m_cancel = true; + wakeup = true; + } + unlockMutex(m_threadMutex); + + // force thread to exit system calls if wakeup is true + if (wakeup) { + pthread_kill(thread->m_thread, SIGWAKEUP); + } +} + +void +ArchMultithreadPosix::setPriorityOfThread(ArchThread thread, int /*n*/) +{ + assert(thread != NULL); + + // FIXME +} + +void +ArchMultithreadPosix::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + ArchThreadImpl* thread = findNoRef(pthread_self()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +ArchMultithreadPosix::wait(ArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + ArchThreadImpl* self = findNoRef(pthread_self()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + try { + // do first test regardless of timeout + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // wait and repeat test if there's a timeout + if (timeout != 0.0) { + const double start = ARCH->time(); + do { + // wait a little + ARCH->sleep(0.05); + + // repeat test + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // repeat wait and test until timed out + } while (timeout < 0.0 || (ARCH->time() - start) <= timeout); + } + + closeThread(target); + return false; + } + catch (...) { + closeThread(target); + throw; + } +} + +bool +ArchMultithreadPosix::isSameThread(ArchThread thread1, ArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +ArchMultithreadPosix::isExitedThread(ArchThread thread) +{ + lockMutex(m_threadMutex); + bool exited = thread->m_exited; + unlockMutex(m_threadMutex); + return exited; +} + +void* +ArchMultithreadPosix::getResultOfThread(ArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +ArchMultithreadPosix::getIDOfThread(ArchThread thread) +{ + return thread->m_id; +} + +void +ArchMultithreadPosix::setSignalHandler( + ESignal signal, SignalFunc func, void* userData) +{ + lockMutex(m_threadMutex); + m_signalFunc[signal] = func; + m_signalUserData[signal] = userData; + unlockMutex(m_threadMutex); +} + +void +ArchMultithreadPosix::raiseSignal(ESignal signal) +{ + lockMutex(m_threadMutex); + if (m_signalFunc[signal] != NULL) { + m_signalFunc[signal](signal, m_signalUserData[signal]); + pthread_kill(m_mainThread->m_thread, SIGWAKEUP); + } + else if (signal == kINTERRUPT || signal == kTERMINATE) { + ARCH->cancelThread(m_mainThread); + } + unlockMutex(m_threadMutex); +} + +void +ArchMultithreadPosix::startSignalHandler() +{ + // set signal mask. the main thread blocks these signals and + // the signal handler thread will listen for them. + sigset_t sigset, oldsigset; + setSignalSet(&sigset); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); + + // fire up the INT and TERM signal handler thread. we could + // instead arrange to catch and handle these signals but + // we'd be unable to cancel the main thread since no pthread + // calls are allowed in a signal handler. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&m_signalThread, &attr, + &ArchMultithreadPosix::threadSignalHandler, + NULL); + pthread_attr_destroy(&attr); + } + if (status != 0) { + // can't create thread to wait for signal so don't block + // the signals. + pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL); + } +} + +ArchThreadImpl* +ArchMultithreadPosix::find(pthread_t thread) +{ + ArchThreadImpl* impl = findNoRef(thread); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +ArchThreadImpl* +ArchMultithreadPosix::findNoRef(pthread_t thread) +{ + // linear search + for (ThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_thread == thread) { + return *index; + } + } + return NULL; +} + +void +ArchMultithreadPosix::insert(ArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRef(thread->m_thread) == NULL); + + // set thread id. note that we don't worry about m_nextID + // wrapping back to 0 and duplicating thread ID's since the + // likelihood of barrier running that long is vanishingly + // small. + thread->m_id = ++m_nextID; + + // append to list + m_threadList.push_back(thread); +} + +void +ArchMultithreadPosix::erase(ArchThreadImpl* thread) +{ + for (ThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +ArchMultithreadPosix::refThread(ArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRef(thread->m_thread) != NULL); + ++thread->m_refCount; +} + +void +ArchMultithreadPosix::testCancelThreadImpl(ArchThreadImpl* thread) +{ + assert(thread != NULL); + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = false; + if (thread->m_cancel && !thread->m_cancelling) { + thread->m_cancelling = true; + thread->m_cancel = false; + cancel = true; + } + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +void* +ArchMultithreadPosix::threadFunc(void* vrep) +{ + // get the thread + ArchThreadImpl* thread = static_cast<ArchThreadImpl*>(vrep); + + // setup pthreads + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return NULL; +} + +void +ArchMultithreadPosix::doThreadFunc(ArchThread thread) +{ + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + lockMutex(m_threadMutex); + thread->m_exited = true; + unlockMutex(m_threadMutex); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + thread->m_exited = true; + unlockMutex(m_threadMutex); + + // done with thread + closeThread(thread); +} + +void +ArchMultithreadPosix::threadCancel(int) +{ + // do nothing +} + +void* +ArchMultithreadPosix::threadSignalHandler(void*) +{ + // detach + pthread_detach(pthread_self()); + + // add signal to mask + sigset_t sigset; + setSignalSet(&sigset); + + // also wait on SIGABRT. on linux (others?) this thread (process) + // will persist after all the other threads evaporate due to an + // assert unless we wait on SIGABRT. that means our resources (like + // the socket we're listening on) are not released and never will be + // until the lingering thread is killed. i don't know why sigwait() + // should protect the thread from being killed. note that sigwait() + // doesn't actually return if we receive SIGABRT and, for some + // reason, we don't have to block SIGABRT. + sigaddset(&sigset, SIGABRT); + + // we exit the loop via thread cancellation in sigwait() + for (;;) { + // wait +#if HAVE_POSIX_SIGWAIT + int signal = 0; + sigwait(&sigset, &signal); +#else + sigwait(&sigset); +#endif + + // if we get here then the signal was raised + switch (signal) { + case SIGINT: + ARCH->raiseSignal(kINTERRUPT); + break; + + case SIGTERM: + ARCH->raiseSignal(kTERMINATE); + break; + + case SIGHUP: + ARCH->raiseSignal(kHANGUP); + break; + + case SIGUSR2: + ARCH->raiseSignal(kUSER); + break; + + default: + // ignore + break; + } + } + + return NULL; +} diff --git a/src/lib/arch/unix/ArchMultithreadPosix.h b/src/lib/arch/unix/ArchMultithreadPosix.h new file mode 100644 index 0000000..98b5eda --- /dev/null +++ b/src/lib/arch/unix/ArchMultithreadPosix.h @@ -0,0 +1,115 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchMultithread.h" +#include "common/stdlist.h" + +#include <pthread.h> + +#define ARCH_MULTITHREAD ArchMultithreadPosix + +class ArchCondImpl { +public: + pthread_cond_t m_cond; +}; + +class ArchMutexImpl { +public: + pthread_mutex_t m_mutex; +}; + +//! Posix implementation of IArchMultithread +class ArchMultithreadPosix : public IArchMultithread { +public: + ArchMultithreadPosix(); + virtual ~ArchMultithreadPosix(); + + //! @name manipulators + //@{ + + void setNetworkDataForCurrentThread(void*); + + //@} + //! @name accessors + //@{ + + void* getNetworkDataForThread(ArchThread); + + static ArchMultithreadPosix* getInstance(); + + //@} + + // IArchMultithread overrides + virtual ArchCond newCondVar(); + virtual void closeCondVar(ArchCond); + virtual void signalCondVar(ArchCond); + virtual void broadcastCondVar(ArchCond); + virtual bool waitCondVar(ArchCond, ArchMutex, double timeout); + virtual ArchMutex newMutex(); + virtual void closeMutex(ArchMutex); + virtual void lockMutex(ArchMutex); + virtual void unlockMutex(ArchMutex); + virtual ArchThread newThread(ThreadFunc, void*); + virtual ArchThread newCurrentThread(); + virtual ArchThread copyThread(ArchThread); + virtual void closeThread(ArchThread); + virtual void cancelThread(ArchThread); + virtual void setPriorityOfThread(ArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(ArchThread, double timeout); + virtual bool isSameThread(ArchThread, ArchThread); + virtual bool isExitedThread(ArchThread); + virtual void* getResultOfThread(ArchThread); + virtual ThreadID getIDOfThread(ArchThread); + virtual void setSignalHandler(ESignal, SignalFunc, void*); + virtual void raiseSignal(ESignal); + +private: + void startSignalHandler(); + + ArchThreadImpl* find(pthread_t thread); + ArchThreadImpl* findNoRef(pthread_t thread); + void insert(ArchThreadImpl* thread); + void erase(ArchThreadImpl* thread); + + void refThread(ArchThreadImpl* rep); + void testCancelThreadImpl(ArchThreadImpl* rep); + + void doThreadFunc(ArchThread thread); + static void* threadFunc(void* vrep); + static void threadCancel(int); + static void* threadSignalHandler(void* vrep); + +private: + typedef std::list<ArchThread> ThreadList; + + static ArchMultithreadPosix* s_instance; + + bool m_newThreadCalled; + + ArchMutex m_threadMutex; + ArchThread m_mainThread; + ThreadList m_threadList; + ThreadID m_nextID; + + pthread_t m_signalThread; + SignalFunc m_signalFunc[kNUM_SIGNALS]; + void* m_signalUserData[kNUM_SIGNALS]; +}; diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp new file mode 100644 index 0000000..149218c --- /dev/null +++ b/src/lib/arch/unix/ArchNetworkBSD.cpp @@ -0,0 +1,1005 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchNetworkBSD.h" + +#include "arch/unix/ArchMultithreadPosix.h" +#include "arch/unix/XArchUnix.h" +#include "arch/Arch.h" + +#if HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <netinet/in.h> +#include <netdb.h> +#if !defined(TCP_NODELAY) +# include <netinet/tcp.h> +#endif +#include <arpa/inet.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#if HAVE_POLL +# include <poll.h> +#else +# if HAVE_SYS_SELECT_H +# include <sys/select.h> +# endif +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# endif +#endif + +#if !HAVE_INET_ATON +# include <stdio.h> +#endif + +static const int s_family[] = { + PF_UNSPEC, + PF_INET, + PF_INET6, +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +#if !HAVE_INET_ATON +// parse dotted quad addresses. we don't bother with the weird BSD'ism +// of handling octal and hex and partial forms. +static +in_addr_t +inet_aton(const char* cp, struct in_addr* inp) +{ + unsigned int a, b, c, d; + if (sscanf(cp, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) { + return 0; + } + if (a >= 256 || b >= 256 || c >= 256 || d >= 256) { + return 0; + } + unsigned char* incp = (unsigned char*)inp; + incp[0] = (unsigned char)(a & 0xffu); + incp[1] = (unsigned char)(b & 0xffu); + incp[2] = (unsigned char)(c & 0xffu); + incp[3] = (unsigned char)(d & 0xffu); + return inp->s_addr; +} +#endif + +// +// ArchNetworkBSD +// + +ArchNetworkBSD::ArchNetworkBSD() +{ +} + +ArchNetworkBSD::~ArchNetworkBSD() +{ + ARCH->closeMutex(m_mutex); +} + +void +ArchNetworkBSD::init() +{ + // create mutex to make some calls thread safe + m_mutex = ARCH->newMutex(); +} + +ArchSocket +ArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) +{ + // create socket + int fd = socket(s_family[family], s_type[type], 0); + if (fd == -1) { + throwError(errno); + } + try { + setBlockingOnSocket(fd, false); + } + catch (...) { + close(fd); + throw; + } + + // allocate socket object + ArchSocketImpl* newSocket = new ArchSocketImpl; + newSocket->m_fd = fd; + newSocket->m_refCount = 1; + return newSocket; +} + +ArchSocket +ArchNetworkBSD::copySocket(ArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +ArchNetworkBSD::closeSocket(ArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + if (close(s->m_fd) == -1) { + // close failed. restore the last ref and throw. + int err = errno; + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + delete s; + } +} + +void +ArchNetworkBSD::closeSocketForRead(ArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 0) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +ArchNetworkBSD::closeSocketForWrite(ArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 1) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +ArchNetworkBSD::bindSocket(ArchSocket s, ArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) { + throwError(errno); + } +} + +void +ArchNetworkBSD::listenOnSocket(ArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen(s->m_fd, 3) == -1) { + throwError(errno); + } +} + +ArchSocket +ArchNetworkBSD::acceptSocket(ArchSocket s, ArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + ArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + ArchSocketImpl* newSocket = new ArchSocketImpl; + *addr = new ArchNetAddressImpl; + + // accept on socket + ACCEPT_TYPE_ARG3 len = (ACCEPT_TYPE_ARG3)((*addr)->m_len); + int fd = accept(s->m_fd, TYPED_ADDR(struct sockaddr, (*addr)), &len); + (*addr)->m_len = (socklen_t)len; + if (fd == -1) { + int err = errno; + delete newSocket; + delete *addr; + *addr = NULL; + if (err == EAGAIN) { + return NULL; + } + throwError(err); + } + + try { + setBlockingOnSocket(fd, false); + } + catch (...) { + close(fd); + delete newSocket; + delete *addr; + *addr = NULL; + throw; + } + + // initialize socket + newSocket->m_fd = fd; + newSocket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return newSocket; +} + +bool +ArchNetworkBSD::connectSocket(ArchSocket s, ArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (connect(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) { + if (errno == EISCONN) { + return true; + } + if (errno == EINPROGRESS) { + return false; + } + throwError(errno); + } + return true; +} + +#if HAVE_POLL + +int +ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) +{ + assert(pe != NULL || num == 0); + + // return if nothing to do + if (num == 0) { + if (timeout > 0.0) { + ARCH->sleep(timeout); + } + return 0; + } + + // allocate space for translated query + struct pollfd* pfd = new struct pollfd[1 + num]; + + // translate query + for (int i = 0; i < num; ++i) { + pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd; + pfd[i].events = 0; + if ((pe[i].m_events & kPOLLIN) != 0) { + pfd[i].events |= POLLIN; + } + if ((pe[i].m_events & kPOLLOUT) != 0) { + pfd[i].events |= POLLOUT; + } + } + int n = num; + + // add the unblock pipe + const int* unblockPipe = getUnblockPipe(); + if (unblockPipe != NULL) { + pfd[n].fd = unblockPipe[0]; + pfd[n].events = POLLIN; + ++n; + } + + // prepare timeout + int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout); + + // do the poll + n = poll(pfd, n, t); + + // reset the unblock pipe + if (n > 0 && unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) { + // the unblock event was signalled. flush the pipe. + char dummy[100]; + int ignore; + + do { + ignore = read(unblockPipe[0], dummy, sizeof(dummy)); + } while (errno != EAGAIN); + + // don't count this unblock pipe in return value + --n; + } + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + delete[] pfd; + return 0; + } + delete[] pfd; + throwError(errno); + } + + // translate back + for (int i = 0; i < num; ++i) { + pe[i].m_revents = 0; + if ((pfd[i].revents & POLLIN) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((pfd[i].revents & POLLOUT) != 0) { + pe[i].m_revents |= kPOLLOUT; + } + if ((pfd[i].revents & POLLERR) != 0) { + pe[i].m_revents |= kPOLLERR; + } + if ((pfd[i].revents & POLLNVAL) != 0) { + pe[i].m_revents |= kPOLLNVAL; + } + } + + delete[] pfd; + return n; +} + +#else + +int +ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) +{ + int i, n; + + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + int fdi = pe[i].m_socket->m_fd; + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_fd, &readSet); + readSetP = &readSet; + if (fdi > n) { + n = fdi; + } + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_fd, &writeSet); + writeSetP = &writeSet; + if (fdi > n) { + n = fdi; + } + } + if (true) { + FD_SET(pe[i].m_socket->m_fd, &errSet); + errSetP = &errSet; + if (fdi > n) { + n = fdi; + } + } + } + + // add the unblock pipe + const int* unblockPipe = getUnblockPipe(); + if (unblockPipe != NULL) { + FD_SET(unblockPipe[0], &readSet); + readSetP = &readSet; + if (unblockPipe[0] > n) { + n = unblockPipe[0]; + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0.0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast<int>(timeout); + timeout2.tv_usec = static_cast<int>(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // reset the unblock pipe + if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) { + // the unblock event was signalled. flush the pipe. + char dummy[100]; + do { + read(unblockPipe[0], dummy, sizeof(dummy)); + } while (errno != EAGAIN); + } + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + return 0; + } + throwError(errno); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + + return n; +} + +#endif + +void +ArchNetworkBSD::unblockPollSocket(ArchThread thread) +{ + const int* unblockPipe = getUnblockPipeForThread(thread); + if (unblockPipe != NULL) { + char dummy = 0; + int ignore; + + ignore = write(unblockPipe[1], &dummy, 1); + } +} + +size_t +ArchNetworkBSD::readSocket(ArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n = read(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) { + return 0; + } + throwError(errno); + } + return n; +} + +size_t +ArchNetworkBSD::writeSocket(ArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n = write(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) { + return 0; + } + throwError(errno); + } + return n; +} + +void +ArchNetworkBSD::throwErrorOnSocket(ArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + socklen_t size = (socklen_t)sizeof(err); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, + (optval_t*)&err, &size) == -1) { + err = errno; + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +void +ArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking) +{ + assert(fd != -1); + + int mode = fcntl(fd, F_GETFL, 0); + if (mode == -1) { + throwError(errno); + } + if (blocking) { + mode &= ~O_NONBLOCK; + } + else { + mode |= O_NONBLOCK; + } + if (fcntl(fd, F_SETFL, mode) == -1) { + throwError(errno); + } +} + +bool +ArchNetworkBSD::setNoDelayOnSocket(ArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = (socklen_t)sizeof(oflag); + if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, + (optval_t*)&oflag, &size) == -1) { + throwError(errno); + } + + int flag = noDelay ? 1 : 0; + size = (socklen_t)sizeof(flag); + if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, + (optval_t*)&flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + +bool +ArchNetworkBSD::setReuseAddrOnSocket(ArchSocket s, bool reuse) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = (socklen_t)sizeof(oflag); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, + (optval_t*)&oflag, &size) == -1) { + throwError(errno); + } + + int flag = reuse ? 1 : 0; + size = (socklen_t)sizeof(flag); + if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, + (optval_t*)&flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + +std::string +ArchNetworkBSD::getHostName() +{ + char name[256]; + if (gethostname(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +ArchNetAddress +ArchNetworkBSD::newAnyAddr(EAddressFamily family) +{ + // allocate address + ArchNetAddressImpl* addr = new ArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = (socklen_t)sizeof(struct sockaddr_in); + break; + } + + case kINET6: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr); + ipAddr->sin6_family = AF_INET6; + ipAddr->sin6_port = 0; + memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)); + addr->m_len = (socklen_t)sizeof(struct sockaddr_in6); + break; + } + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +ArchNetAddress +ArchNetworkBSD::copyAddr(ArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new ArchNetAddressImpl(*addr); +} + +ArchNetAddress +ArchNetworkBSD::nameToAddr(const std::string& name) +{ + // allocate address + ArchNetAddressImpl* addr = new ArchNetAddressImpl; + + char ipstr[INET6_ADDRSTRLEN]; + struct addrinfo hints; + struct addrinfo *p; + int ret; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + + ARCH->lockMutex(m_mutex); + if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &p)) != 0) { + ARCH->unlockMutex(m_mutex); + delete addr; + throwNameError(ret); + } + + if (p->ai_family == AF_INET) { + addr->m_len = (socklen_t)sizeof(struct sockaddr_in); + } else { + addr->m_len = (socklen_t)sizeof(struct sockaddr_in6); + } + + memcpy(&addr->m_addr, p->ai_addr, addr->m_len); + freeaddrinfo(p); + ARCH->unlockMutex(m_mutex); + + return addr; +} + +void +ArchNetworkBSD::closeAddr(ArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +ArchNetworkBSD::addrToName(ArchNetAddress addr) +{ + assert(addr != NULL); + + // mutexed name lookup (ugh) + ARCH->lockMutex(m_mutex); + char host[1024]; + char service[20]; + int ret = getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host, + sizeof(host), service, sizeof(service), 0); + if (ret != 0) { + ARCH->unlockMutex(m_mutex); + throwNameError(ret); + } + + // save (primary) name + std::string name = host; + + // done with static buffer + ARCH->unlockMutex(m_mutex); + + return name; +} + +std::string +ArchNetworkBSD::addrToString(ArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); + ARCH->lockMutex(m_mutex); + std::string s = inet_ntoa(ipAddr->sin_addr); + ARCH->unlockMutex(m_mutex); + return s; + } + + case kINET6: { + char strAddr[INET6_ADDRSTRLEN]; + auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr); + ARCH->lockMutex(m_mutex); + inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN); + ARCH->unlockMutex(m_mutex); + return strAddr; + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +ArchNetworkBSD::getAddrFamily(ArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.ss_family) { + case AF_INET: + return kINET; + case AF_INET6: + return kINET6; + + default: + return kUNKNOWN; + } +} + +void +ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); + ipAddr->sin_port = htons(port); + break; + } + + case kINET6: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr); + ipAddr->sin6_port = htons(port); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +ArchNetworkBSD::getAddrPort(ArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); + return ntohs(ipAddr->sin_port); + } + + case kINET6: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr); + return ntohs(ipAddr->sin6_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +ArchNetworkBSD::isAnyAddr(ArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == (socklen_t)sizeof(struct sockaddr_in)); + } + + case kINET6: { + auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr); + return (memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 && + addr->m_len == (socklen_t)sizeof(struct sockaddr_in6)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +bool +ArchNetworkBSD::isEqualAddr(ArchNetAddress a, ArchNetAddress b) +{ + return (a->m_len == b->m_len && + memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0); +} + +const int* +ArchNetworkBSD::getUnblockPipe() +{ + ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance(); + ArchThread thread = mt->newCurrentThread(); + const int* p = getUnblockPipeForThread(thread); + ARCH->closeThread(thread); + return p; +} + +const int* +ArchNetworkBSD::getUnblockPipeForThread(ArchThread thread) +{ + ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance(); + int* unblockPipe = (int*)mt->getNetworkDataForThread(thread); + if (unblockPipe == NULL) { + unblockPipe = new int[2]; + if (pipe(unblockPipe) != -1) { + try { + setBlockingOnSocket(unblockPipe[0], false); + mt->setNetworkDataForCurrentThread(unblockPipe); + } + catch (...) { + delete[] unblockPipe; + unblockPipe = NULL; + } + } + else { + delete[] unblockPipe; + unblockPipe = NULL; + } + } + return unblockPipe; +} + +void +ArchNetworkBSD::throwError(int err) +{ + switch (err) { + case EINTR: + ARCH->testCancelThread(); + throw XArchNetworkInterrupted(new XArchEvalUnix(err)); + + case EACCES: + case EPERM: + throw XArchNetworkAccess(new XArchEvalUnix(err)); + + case ENFILE: + case EMFILE: + case ENODEV: + case ENOBUFS: + case ENOMEM: + case ENETDOWN: +#if defined(ENOSR) + case ENOSR: +#endif + throw XArchNetworkResource(new XArchEvalUnix(err)); + + case EPROTOTYPE: + case EPROTONOSUPPORT: + case EAFNOSUPPORT: + case EPFNOSUPPORT: + case ESOCKTNOSUPPORT: + case EINVAL: + case ENOPROTOOPT: + case EOPNOTSUPP: + case ESHUTDOWN: +#if defined(ENOPKG) + case ENOPKG: +#endif + throw XArchNetworkSupport(new XArchEvalUnix(err)); + + case EIO: + throw XArchNetworkIO(new XArchEvalUnix(err)); + + case EADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalUnix(err)); + + case EADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalUnix(err)); + + case EHOSTUNREACH: + case ENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalUnix(err)); + + case ENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalUnix(err)); + + case EPIPE: + throw XArchNetworkShutdown(new XArchEvalUnix(err)); + + case ECONNABORTED: + case ECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalUnix(err)); + + case ECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalUnix(err)); + + case EHOSTDOWN: + case ETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalUnix(err)); + + default: + throw XArchNetwork(new XArchEvalUnix(err)); + } +} + +void +ArchNetworkBSD::throwNameError(int err) +{ + static const char* s_msg[] = { + "The specified host is unknown", + "The requested name is valid but does not have an IP address", + "A non-recoverable name server error occurred", + "A temporary error occurred on an authoritative name server", + "An unknown name server error occurred" + }; + + switch (err) { + case HOST_NOT_FOUND: + throw XArchNetworkNameUnknown(s_msg[0]); + + case NO_DATA: + throw XArchNetworkNameNoAddress(s_msg[1]); + + case NO_RECOVERY: + throw XArchNetworkNameFailure(s_msg[2]); + + case TRY_AGAIN: + throw XArchNetworkNameUnavailable(s_msg[3]); + + default: + throw XArchNetworkName(s_msg[4]); + } +} diff --git a/src/lib/arch/unix/ArchNetworkBSD.h b/src/lib/arch/unix/ArchNetworkBSD.h new file mode 100644 index 0000000..3f5679a --- /dev/null +++ b/src/lib/arch/unix/ArchNetworkBSD.h @@ -0,0 +1,105 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchNetwork.h" +#include "arch/IArchMultithread.h" + +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_SOCKET_H +# include <sys/socket.h> +#endif + +#if !HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + +// old systems may use char* for [gs]etsockopt()'s optval argument. +// this should be void on modern systems but char is forwards +// compatible so we always use it. +typedef char optval_t; + +#define ARCH_NETWORK ArchNetworkBSD +#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_*>(&addr_->m_addr)) + +class ArchSocketImpl { +public: + int m_fd; + int m_refCount; +}; + +class ArchNetAddressImpl { +public: + ArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr_storage m_addr; + socklen_t m_len; +}; + +//! Berkeley (BSD) sockets implementation of IArchNetwork +class ArchNetworkBSD : public IArchNetwork { +public: + ArchNetworkBSD(); + virtual ~ArchNetworkBSD(); + + virtual void init(); + + // IArchNetwork overrides + virtual ArchSocket newSocket(EAddressFamily, ESocketType); + virtual ArchSocket copySocket(ArchSocket s); virtual void closeSocket(ArchSocket s); + virtual void closeSocketForRead(ArchSocket s); + virtual void closeSocketForWrite(ArchSocket s); + virtual void bindSocket(ArchSocket s, ArchNetAddress addr); + virtual void listenOnSocket(ArchSocket s); + virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr); + virtual bool connectSocket(ArchSocket s, ArchNetAddress name); + virtual int pollSocket(PollEntry[], int num, double timeout); + virtual void unblockPollSocket(ArchThread thread); + virtual size_t readSocket(ArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(ArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(ArchSocket); + virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay); + virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse); + virtual std::string getHostName(); + virtual ArchNetAddress newAnyAddr(EAddressFamily); + virtual ArchNetAddress copyAddr(ArchNetAddress); + virtual ArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(ArchNetAddress); + virtual std::string addrToName(ArchNetAddress); + virtual std::string addrToString(ArchNetAddress); + virtual EAddressFamily getAddrFamily(ArchNetAddress); + virtual void setAddrPort(ArchNetAddress, int port); + virtual int getAddrPort(ArchNetAddress); + virtual bool isAnyAddr(ArchNetAddress); + virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress); + +private: + const int* getUnblockPipe(); + const int* getUnblockPipeForThread(ArchThread); + void setBlockingOnSocket(int fd, bool blocking); + void throwError(int); + void throwNameError(int); + +private: + ArchMutex m_mutex; +}; diff --git a/src/lib/arch/unix/ArchSleepUnix.cpp b/src/lib/arch/unix/ArchSleepUnix.cpp new file mode 100644 index 0000000..48e2600 --- /dev/null +++ b/src/lib/arch/unix/ArchSleepUnix.cpp @@ -0,0 +1,94 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchSleepUnix.h" + +#include "arch/Arch.h" + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#if !HAVE_NANOSLEEP +# if HAVE_SYS_SELECT_H +# include <sys/select.h> +# endif +# if HAVE_SYS_TYPES_H +# include <sys/types.h> +# endif +# if HAVE_UNISTD_H +# include <unistd.h> +# endif +#endif + +// +// ArchSleepUnix +// + +ArchSleepUnix::ArchSleepUnix() +{ + // do nothing +} + +ArchSleepUnix::~ArchSleepUnix() +{ + // do nothing +} + +void +ArchSleepUnix::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + +#if HAVE_NANOSLEEP + // prep timeout + struct timespec t; + t.tv_sec = (long)timeout; + t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec)); + + // wait + while (nanosleep(&t, &t) < 0) + ARCH->testCancelThread(); +#else + /* emulate nanosleep() with select() */ + double startTime = ARCH->time(); + double timeLeft = timeout; + while (timeLeft > 0.0) { + struct timeval timeout2; + timeout2.tv_sec = static_cast<int>(timeLeft); + timeout2.tv_usec = static_cast<int>(1.0e+6 * (timeLeft - + timeout2.tv_sec)); + select((SELECT_TYPE_ARG1) 0, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 &timeout2); + ARCH->testCancelThread(); + timeLeft = timeout - (ARCH->time() - startTime); + } +#endif +} diff --git a/src/lib/arch/unix/ArchSleepUnix.h b/src/lib/arch/unix/ArchSleepUnix.h new file mode 100644 index 0000000..3e307a5 --- /dev/null +++ b/src/lib/arch/unix/ArchSleepUnix.h @@ -0,0 +1,33 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchSleep.h" + +#define ARCH_SLEEP ArchSleepUnix + +//! Unix implementation of IArchSleep +class ArchSleepUnix : public IArchSleep { +public: + ArchSleepUnix(); + virtual ~ArchSleepUnix(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; diff --git a/src/lib/arch/unix/ArchStringUnix.cpp b/src/lib/arch/unix/ArchStringUnix.cpp new file mode 100644 index 0000000..591c826 --- /dev/null +++ b/src/lib/arch/unix/ArchStringUnix.cpp @@ -0,0 +1,42 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchStringUnix.h" + +#include <stdio.h> + +// +// ArchStringUnix +// + +#include "arch/multibyte.h" +#include "arch/vsnprintf.h" + +ArchStringUnix::ArchStringUnix() +{ +} + +ArchStringUnix::~ArchStringUnix() +{ +} + +IArchString::EWideCharEncoding +ArchStringUnix::getWideCharEncoding() +{ + return kUCS4; +} diff --git a/src/lib/arch/unix/ArchStringUnix.h b/src/lib/arch/unix/ArchStringUnix.h new file mode 100644 index 0000000..f7d0035 --- /dev/null +++ b/src/lib/arch/unix/ArchStringUnix.h @@ -0,0 +1,34 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchString.h" + +#define ARCH_STRING ArchStringUnix + +//! Unix implementation of IArchString +class ArchStringUnix : public IArchString { +public: + ArchStringUnix(); + virtual ~ArchStringUnix(); + + // IArchString overrides + virtual EWideCharEncoding + getWideCharEncoding(); +}; diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp new file mode 100644 index 0000000..f51e47f --- /dev/null +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -0,0 +1,80 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchSystemUnix.h" + +#include <sys/utsname.h> + +// +// ArchSystemUnix +// + +ArchSystemUnix::ArchSystemUnix() +{ + // do nothing +} + +ArchSystemUnix::~ArchSystemUnix() +{ + // do nothing +} + +std::string +ArchSystemUnix::getOSName() const +{ +#if defined(HAVE_SYS_UTSNAME_H) + struct utsname info; + if (uname(&info) == 0) { + std::string msg; + msg += info.sysname; + msg += " "; + msg += info.release; + return msg; + } +#endif + return "Unix"; +} + +std::string +ArchSystemUnix::getPlatformName() const +{ +#if defined(HAVE_SYS_UTSNAME_H) + struct utsname info; + if (uname(&info) == 0) { + return std::string(info.machine); + } +#endif + return "unknown"; +} + +std::string +ArchSystemUnix::setting(const std::string&) const +{ + return ""; +} + +void +ArchSystemUnix::setting(const std::string&, const std::string&) const +{ +} + +std::string +ArchSystemUnix::getLibsUsed(void) const +{ + return "not implemented.\nuse lsof on shell"; +} diff --git a/src/lib/arch/unix/ArchSystemUnix.h b/src/lib/arch/unix/ArchSystemUnix.h new file mode 100644 index 0000000..aa9c564 --- /dev/null +++ b/src/lib/arch/unix/ArchSystemUnix.h @@ -0,0 +1,38 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchSystem.h" + +#define ARCH_SYSTEM ArchSystemUnix + +//! Unix implementation of IArchString +class ArchSystemUnix : public IArchSystem { +public: + ArchSystemUnix(); + virtual ~ArchSystemUnix(); + + // IArchSystem overrides + virtual std::string getOSName() const; + virtual std::string getPlatformName() const; + virtual std::string setting(const std::string&) const; + virtual void setting(const std::string&, const std::string&) const; + virtual std::string getLibsUsed(void) const; + +}; diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.cpp b/src/lib/arch/unix/ArchTaskBarXWindows.cpp new file mode 100644 index 0000000..c3577ad --- /dev/null +++ b/src/lib/arch/unix/ArchTaskBarXWindows.cpp @@ -0,0 +1,51 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchTaskBarXWindows.h" + +// +// ArchTaskBarXWindows +// + +ArchTaskBarXWindows::ArchTaskBarXWindows() +{ + // do nothing +} + +ArchTaskBarXWindows::~ArchTaskBarXWindows() +{ + // do nothing +} + +void +ArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +ArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +ArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.h b/src/lib/arch/unix/ArchTaskBarXWindows.h new file mode 100644 index 0000000..f2c8977 --- /dev/null +++ b/src/lib/arch/unix/ArchTaskBarXWindows.h @@ -0,0 +1,35 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchTaskBar.h" + +#define ARCH_TASKBAR ArchTaskBarXWindows + +//! X11 implementation of IArchTaskBar +class ArchTaskBarXWindows : public IArchTaskBar { +public: + ArchTaskBarXWindows(); + virtual ~ArchTaskBarXWindows(); + + // IArchTaskBar overrides + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); +}; diff --git a/src/lib/arch/unix/ArchTimeUnix.cpp b/src/lib/arch/unix/ArchTimeUnix.cpp new file mode 100644 index 0000000..24685aa --- /dev/null +++ b/src/lib/arch/unix/ArchTimeUnix.cpp @@ -0,0 +1,52 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/ArchTimeUnix.h" + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +// +// ArchTimeUnix +// + +ArchTimeUnix::ArchTimeUnix() +{ + // do nothing +} + +ArchTimeUnix::~ArchTimeUnix() +{ + // do nothing +} + +double +ArchTimeUnix::time() +{ + struct timeval t; + gettimeofday(&t, NULL); + return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; +} diff --git a/src/lib/arch/unix/ArchTimeUnix.h b/src/lib/arch/unix/ArchTimeUnix.h new file mode 100644 index 0000000..3c5c0f8 --- /dev/null +++ b/src/lib/arch/unix/ArchTimeUnix.h @@ -0,0 +1,33 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/IArchTime.h" + +#define ARCH_TIME ArchTimeUnix + +//! Generic Unix implementation of IArchTime +class ArchTimeUnix : public IArchTime { +public: + ArchTimeUnix(); + virtual ~ArchTimeUnix(); + + // IArchTime overrides + virtual double time(); +}; diff --git a/src/lib/arch/unix/XArchUnix.cpp b/src/lib/arch/unix/XArchUnix.cpp new file mode 100644 index 0000000..fc7ff65 --- /dev/null +++ b/src/lib/arch/unix/XArchUnix.cpp @@ -0,0 +1,32 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "arch/unix/XArchUnix.h" + +#include <cstring> + +// +// XArchEvalUnix +// + +std::string +XArchEvalUnix::eval() const +{ + // FIXME -- not thread safe + return strerror(m_error); +} diff --git a/src/lib/arch/unix/XArchUnix.h b/src/lib/arch/unix/XArchUnix.h new file mode 100644 index 0000000..ae62f4c --- /dev/null +++ b/src/lib/arch/unix/XArchUnix.h @@ -0,0 +1,33 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "arch/XArch.h" + +//! Lazy error message string evaluation for unix +class XArchEvalUnix : public XArchEval { +public: + XArchEvalUnix(int error) : m_error(error) { } + virtual ~XArchEvalUnix() _NOEXCEPT { } + + virtual std::string eval() const; + +private: + int m_error; +}; |
