pcloudcc-lneely/lib/pclsync/pcompat.c

2596 lines
65 KiB
C

/*
Copyright (c) 2013 Anton Titov.
Copyright (c) 2013 pCloud Ltd. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met: Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer. Redistributions in binary form must reproduce the
above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or materials provided with the
distribution. Neither the name of pCloud Ltd nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud
Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <time.h>
#include "pcompat.h"
#include "psynclib.h"
#include "plibs.h"
#include "psettings.h"
#include "pssl.h"
#include "ptimer.h"
#if defined(P_OS_LINUX)
#include <sys/sysinfo.h>
#endif // P_OS_LINUX
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/statvfs.h>
#include <sys/utsname.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <utime.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
extern char **environ;
#if defined(MAP_ANONYMOUS)
#define PSYNC_MAP_ANONYMOUS MAP_ANONYMOUS
#elif defined(MAP_ANON)
#define PSYNC_MAP_ANONYMOUS MAP_ANON
#endif
#define PROXY_NONE 0
#define PROXY_CONNECT 1
typedef struct {
psync_thread_start0 run;
const char *name;
} psync_run_data0;
typedef struct {
psync_thread_start1 run;
void *ptr;
const char *name;
} psync_run_data1;
static uid_t psync_uid;
static gid_t psync_gid;
static gid_t *psync_gids;
static int psync_gids_cnt;
static int proxy_type=PROXY_NONE;
static int proxy_detected=0;
static char proxy_host[256];
static char proxy_port[8];
static int psync_page_size;
static const char *psync_software_name=PSYNC_LIB_VERSION;
static const char *psync_os_name=NULL;
PSYNC_THREAD const char *psync_thread_name="no name";
static pthread_mutex_t socket_mutex=PTHREAD_MUTEX_INITIALIZER;
const unsigned char psync_invalid_filename_chars[256]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
int
psync_user_is_admin(){
return 0;
}
void
psync_compat_init(){
struct rlimit limit;
limit.rlim_cur=limit.rlim_max=2048;
if (setrlimit(RLIMIT_NOFILE, &limit))
debug(D_ERROR, "setrlimit failed errno=%d", errno);
#if IS_DEBUG
if (getrlimit(RLIMIT_CORE, &limit))
debug(D_ERROR, "getrlimit failed errno=%d", errno);
else{
limit.rlim_cur=limit.rlim_max;
if (setrlimit(RLIMIT_CORE, &limit))
debug(D_ERROR, "setrlimit failed errno=%d", errno);
}
#endif
signal(SIGPIPE, SIG_IGN);
psync_uid=getuid();
psync_gid=getgid();
psync_gids_cnt=getgroups(0, NULL);
psync_gids=psync_new_cnt(gid_t, psync_gids_cnt);
if (unlikely_log(getgroups(psync_gids_cnt, psync_gids)!=psync_gids_cnt))
psync_gids_cnt=0;
#if defined(PAGESIZE)
psync_page_size=PAGESIZE;
#else
psync_page_size=sysconf(_SC_PAGESIZE);
#endif
debug(D_NOTICE, "detected page size %d", psync_page_size);
}
int
psync_stat_mode_ok(psync_stat_t *buf, unsigned int bits){
int i;
if (psync_uid==0)
return 1;
if (buf->st_uid==psync_uid){
bits<<=6;
return (buf->st_mode&bits)==bits;
}
if (buf->st_gid==psync_gid){
bits<<=3;
return (buf->st_mode&bits)==bits;
}
for (i=0; i<psync_gids_cnt; i++)
if (buf->st_gid==psync_gids[i]){
bits<<=3;
return (buf->st_mode&bits)==bits;
}
return (buf->st_mode&bits)==bits;
}
char *
psync_get_default_database_path_old(){
struct stat st;
const char *dir;
dir=getenv("HOME");
if (unlikely_log(!dir) || unlikely_log(stat(dir, &st)) || unlikely_log(!psync_stat_mode_ok(&st, 7))){
struct passwd pwd;
struct passwd *result;
char buff[4096];
if (unlikely_log(getpwuid_r(getuid(), &pwd, buff, sizeof(buff), &result)) || unlikely_log(stat(result->pw_dir, &st)) ||
unlikely_log(!psync_stat_mode_ok(&st, 7)))
return NULL;
dir=result->pw_dir;
}
return psync_strcat(dir, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_POSIX_DBNAME, NULL);
}
static char *
psync_get_pcloud_path_nc(){
struct stat st;
const char *dir;
dir=getenv("HOME");
if (unlikely_log(!dir) || unlikely_log(stat(dir, &st)) || unlikely_log(!psync_stat_mode_ok(&st, 7))){
struct passwd pwd;
struct passwd *result;
char buff[4096];
if (unlikely_log(getpwuid_r(getuid(), &pwd, buff, sizeof(buff), &result)) || unlikely_log(stat(result->pw_dir, &st)) ||
unlikely_log(!psync_stat_mode_ok(&st, 7)))
return NULL;
dir=result->pw_dir;
}
return psync_strcat(dir, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_POSIX_DIR, NULL);
}
char *
psync_get_pcloud_path(){
char *path;
psync_stat_t st;
path=psync_get_pcloud_path_nc();
if (unlikely_log(!path))
return NULL;
if (psync_stat(path, &st) && unlikely_log(psync_mkdir(path))){
psync_free(path);
return NULL;
}
return path;
}
char *
psync_get_private_dir(char *name){
char *path, *rpath;
psync_stat_t st;
path=psync_get_pcloud_path();
if (!path)
return NULL;
rpath=psync_strcat(path, PSYNC_DIRECTORY_SEPARATOR, name, NULL);
free(path);
if (psync_stat(rpath, &st) && psync_mkdir(rpath)){
psync_free(rpath);
return NULL;
}
return rpath;
}
char *
psync_get_private_tmp_dir(){
return psync_get_private_dir(PSYNC_DEFAULT_TMP_DIR);
}
char *
psync_get_default_database_path(){
char *dirpath, *path;
psync_stat_t st;
dirpath=psync_get_pcloud_path();
if (!dirpath)
return NULL;
path=psync_strcat(dirpath, PSYNC_DIRECTORY_SEPARATOR, PSYNC_DEFAULT_DB_NAME, NULL);
psync_free(dirpath);
if (psync_stat(path, &st) && (dirpath=psync_get_default_database_path_old())){
if (!psync_stat(dirpath, &st)){
if (psync_sql_reopen(dirpath)){
psync_free(path);
return dirpath;
}
else
psync_file_rename(dirpath, path);
}
psync_free(dirpath);
}
return path;
}
char *
psync_get_home_dir(){
struct stat st;
const char *dir;
dir=getenv("HOME");
if (unlikely_log(!dir) || unlikely_log(stat(dir, &st)) || unlikely_log(!psync_stat_mode_ok(&st, 7))){
struct passwd pwd;
struct passwd *result;
char buff[4096];
if (unlikely_log(getpwuid_r(getuid(), &pwd, buff, sizeof(buff), &result)) || unlikely_log(stat(result->pw_dir, &st)) ||
unlikely_log(!psync_stat_mode_ok(&st, 7)))
return NULL;
dir=result->pw_dir;
}
return psync_strdup(dir);
}
void
psync_yield_cpu(){
sched_yield();
}
static void
thread_started(){
//debug(D_NOTICE, "thread started"); //This repeats too many times because of the overlays
}
static void
thread_exited(){
// debug(D_NOTICE, "thread exited"); //This repeats too many times because of the overlays
}
static void *
thread_entry0(void *data){
psync_thread_start0 run;
run=((psync_run_data0 *)data)->run;
psync_thread_name=((psync_run_data0 *)data)->name;
psync_free(data);
thread_started();
run();
thread_exited();
return NULL;
}
void
psync_run_thread(const char *name, psync_thread_start0 run){
psync_run_data0 *data;
pthread_t thread;
pthread_attr_t attr;
data=psync_new(psync_run_data0);
data->run=run;
data->name=name;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, PSYNC_STACK_SIZE);
pthread_create(&thread, &attr, thread_entry0, data);
pthread_attr_destroy(&attr);
}
static void *
thread_entry1(void *data){
psync_thread_start1 run;
void *ptr;
run=((psync_run_data1 *)data)->run;
ptr=((psync_run_data1 *)data)->ptr;
psync_thread_name=((psync_run_data1 *)data)->name;
psync_free(data);
thread_started();
run(ptr);
thread_exited();
return NULL;
}
void
psync_run_thread1(const char *name, psync_thread_start1 run, void *ptr){
psync_run_data1 *data;
pthread_t thread;
pthread_attr_t attr;
data=psync_new(psync_run_data1);
data->run=run;
data->ptr=ptr;
data->name=name;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, PSYNC_STACK_SIZE);
pthread_create(&thread, &attr, thread_entry1, data);
pthread_attr_destroy(&attr);
}
static void
psync_check_no_sql_lock(uint64_t millisec){
#if IS_DEBUG
if (psync_sql_islocked()){
debug(D_CRITICAL, "trying to sleep while holding sql lock, aborting");
psync_sql_dump_locks();
abort();
}
#endif
}
void
psync_milisleep_nosqlcheck(uint64_t millisec){
struct timespec tm;
tm.tv_sec=millisec/1000;
tm.tv_nsec=(millisec%1000)*1000000;
nanosleep(&tm, NULL);
}
void
psync_milisleep(uint64_t millisec){
psync_check_no_sql_lock(millisec);
psync_milisleep_nosqlcheck(millisec);
}
time_t
psync_time(){
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS>0
struct timespec ts;
if (likely_log(clock_gettime(CLOCK_REALTIME, &ts)==0))
return ts.tv_sec;
else
return time(NULL);
#else
return time(NULL);
#endif
}
void
psync_nanotime(struct timespec *tm){
clock_gettime(CLOCK_REALTIME, tm);
}
uint64_t
psync_millitime(){
struct timespec tm;
psync_nanotime(&tm);
return tm.tv_sec*1000+tm.tv_nsec/1000000;
}
static void
psync_add_file_to_seed(const char *fn, psync_lhash_ctx *hctx, size_t max){
char buff[4096];
ssize_t rd;
int fd, mode;
mode=O_RDONLY;
#if defined(O_NONBLOCK)
mode+=O_NONBLOCK;
#elif defined(O_NDELAY)
mode+=O_NDELAY;
#endif
fd=open(fn, mode);
if (fd!=-1){
if (!max || max>sizeof(buff))
max=sizeof(buff);
rd=read(fd, buff, max);
if (rd>0)
psync_lhash_update(hctx, buff, rd);
close(fd);
}
}
#if defined(P_OS_LINUX)
static void
psync_get_random_seed_linux(psync_lhash_ctx *hctx){
struct sysinfo si;
if (likely_log(!sysinfo(&si)))
psync_lhash_update(hctx, &si, sizeof(si));
psync_add_file_to_seed("/proc/stat", hctx, 0);
psync_add_file_to_seed("/proc/vmstat", hctx, 0);
psync_add_file_to_seed("/proc/meminfo", hctx, 0);
psync_add_file_to_seed("/proc/modules", hctx, 0);
psync_add_file_to_seed("/proc/mounts", hctx, 0);
psync_add_file_to_seed("/proc/diskstats", hctx, 0);
psync_add_file_to_seed("/proc/interrupts", hctx, 0);
psync_add_file_to_seed("/proc/net/dev", hctx, 0);
psync_add_file_to_seed("/proc/net/arp", hctx, 0);
}
#endif
static void
psync_get_random_seed_from_query(psync_lhash_ctx *hctx, psync_sql_res *res){
psync_variant_row row;
struct timespec tm;
int i;
while ((row=psync_sql_fetch_row(res))){
for (i=0; i<res->column_count; i++)
if (row[i].type==PSYNC_TSTRING)
psync_lhash_update(hctx, row[i].str, row[i].length);
psync_lhash_update(hctx, row, sizeof(psync_variant)*res->column_count);
}
psync_sql_free_result(res);
psync_nanotime(&tm);
psync_lhash_update(hctx, &tm, sizeof(&tm));
}
static void
psync_get_random_seed_from_db(psync_lhash_ctx *hctx){
psync_sql_res *res;
struct timespec tm;
unsigned char rnd[PSYNC_LHASH_DIGEST_LEN];
psync_nanotime(&tm);
psync_lhash_update(hctx, &tm, sizeof(&tm));
res=psync_sql_query_rdlock("SELECT * FROM setting ORDER BY RANDOM()");
psync_get_random_seed_from_query(hctx, res);
res=psync_sql_query_rdlock("SELECT * FROM resolver ORDER BY RANDOM() LIMIT 50");
psync_get_random_seed_from_query(hctx, res);
psync_sql_statement("REPLACE INTO setting (id, value) VALUES ('random', RANDOM())");
psync_nanotime(&tm);
psync_lhash_update(hctx, &tm, sizeof(&tm));
psync_sql_sync();
psync_nanotime(&tm);
psync_lhash_update(hctx, &tm, sizeof(&tm));
sqlite3_randomness(sizeof(rnd), rnd);
psync_lhash_update(hctx, rnd, sizeof(rnd));
}
static void
psync_rehash_cnt(unsigned char *hashbin, psync_uint_t cnt){
psync_lhash_ctx hctx;
psync_uint_t i;
struct timespec tm;
for (i=0; i<cnt; i++){
psync_lhash_init(&hctx);
if ((i&511)==0){
psync_nanotime(&tm);
psync_lhash_update(&hctx, &tm, sizeof(&tm));
}
else
psync_lhash_update(&hctx, &i, sizeof(i));
psync_lhash_update(&hctx, hashbin, PSYNC_LHASH_DIGEST_LEN);
psync_lhash_final(hashbin, &hctx);
}
}
static void
psync_store_seed_in_db(const unsigned char *seed){
psync_sql_res *res;
unsigned char hashbin[PSYNC_LHASH_DIGEST_LEN];
char hashhex[PSYNC_LHASH_DIGEST_HEXLEN], nm[16];
memcpy(hashbin, seed, PSYNC_LHASH_DIGEST_LEN);
psync_rehash_cnt(hashbin, 2000);
psync_binhex(hashhex, hashbin, PSYNC_LHASH_DIGEST_LEN);
res=psync_sql_prep_statement("REPLACE INTO setting (id, value) VALUES ('randomhash', ?)");
psync_sql_bind_lstring(res, 1, hashhex, PSYNC_LHASH_DIGEST_HEXLEN);
psync_sql_run_free(res);
psync_rehash_cnt(hashbin, 2000);
psync_binhex(hashhex, hashbin, PSYNC_LHASH_DIGEST_LEN);
memcpy(nm, "randomhash", 10);
nm[10]=hashhex[0];
nm[11]=0;
res=psync_sql_prep_statement("REPLACE INTO setting (id, value) VALUES (?, ?)");
psync_sql_bind_lstring(res, 1, nm, 11);
psync_sql_bind_lstring(res, 2, hashhex, PSYNC_LHASH_DIGEST_HEXLEN);
psync_sql_run_free(res);
}
void
psync_get_random_seed(unsigned char *seed, const void *addent, size_t aelen, int fast){
static unsigned char lastseed[PSYNC_LHASH_DIGEST_LEN];
psync_lhash_ctx hctx;
struct timespec tm;
psync_stat_t st;
char *home;
void *ptr;
psync_uint_t i, j;
int64_t i64;
pthread_t threadid;
unsigned char lsc[64][PSYNC_LHASH_DIGEST_LEN];
debug(D_NOTICE, "in");
struct utsname un;
struct statvfs stfs;
char **env;
pid_t pid;
psync_nanotime(&tm);
psync_lhash_init(&hctx);
psync_lhash_update(&hctx, &tm, sizeof(tm));
if (likely_log(!uname(&un)))
psync_lhash_update(&hctx, &un, sizeof(un));
pid=getpid();
psync_lhash_update(&hctx, &pid, sizeof(pid));
if (!statvfs("/", &stfs))
psync_lhash_update(&hctx, &stfs, sizeof(stfs));
for (env=environ; *env!=NULL; env++)
psync_lhash_update(&hctx, *env, strlen(*env));
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK)
if (likely_log(!clock_gettime(CLOCK_MONOTONIC, &tm)))
psync_lhash_update(&hctx, &tm, sizeof(tm));
#endif
#if defined(P_OS_LINUX)
psync_add_file_to_seed("/dev/urandom", &hctx, PSYNC_HASH_DIGEST_LEN);
#else
psync_add_file_to_seed("/dev/random", &hctx, PSYNC_HASH_DIGEST_LEN);
#endif
#if defined(P_OS_LINUX)
psync_get_random_seed_linux(&hctx);
#endif
threadid=pthread_self();
psync_lhash_update(&hctx, &threadid, sizeof(threadid));
ptr=(void *)&ptr;
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
ptr=(void *)psync_get_random_seed;
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
ptr=(void *)pthread_self;
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
ptr=(void *)malloc;
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
ptr=(void *)&lastseed;
psync_lhash_update(&hctx, &ptr, sizeof(ptr));
home=psync_get_home_dir();
if (home){
i64=psync_get_free_space_by_path(home);
psync_lhash_update(&hctx, &i64, sizeof(i64));
psync_lhash_update(&hctx, home, strlen(home));
if (likely_log(!psync_stat(home, &st)))
psync_lhash_update(&hctx, &st, sizeof(st));
psync_free(home);
}
if (!fast){
debug(D_NOTICE, "db in");
psync_get_random_seed_from_db(&hctx);
debug(D_NOTICE, "db out");
}
if (aelen)
psync_lhash_update(&hctx, addent, aelen);
debug(D_NOTICE, "adding bulk data");
for (i=0; i<ARRAY_SIZE(lsc); i++){
memcpy(&lsc[i], lastseed, PSYNC_LHASH_DIGEST_LEN);
for (j=0; j<PSYNC_LHASH_DIGEST_LEN; j++)
lsc[i][j]^=(unsigned char)i;
}
for (j=fast?3:0; j<5; j++){
for (i=0; i<100; i++){
psync_lhash_update(&hctx, &i, sizeof(i));
psync_lhash_update(&hctx, &j, sizeof(j));
psync_lhash_update(&hctx, lsc, sizeof(lsc));
}
psync_nanotime(&tm);
psync_lhash_update(&hctx, &tm, sizeof(&tm));
}
psync_lhash_final(seed, &hctx);
memcpy(lastseed, seed, PSYNC_LHASH_DIGEST_LEN);
debug(D_NOTICE, "storing in db");
psync_store_seed_in_db(seed);
debug(D_NOTICE, "out");
}
static int
psync_wait_socket_writable_microsec(psync_socket_t sock, long sec, long usec){
fd_set wfds;
struct timeval tv;
int res;
tv.tv_sec=sec;
tv.tv_usec=usec;
FD_ZERO(&wfds);
FD_SET(sock, &wfds);
res=select(sock+1, NULL, &wfds, NULL, &tv);
if (res==1)
return 0;
if (res==0)
psync_sock_set_err(P_TIMEDOUT);
return SOCKET_ERROR;
}
#define psync_wait_socket_writable(sock, sec) psync_wait_socket_writable_microsec(sock, sec, 0)
int
psync_wait_socket_write_timeout(psync_socket_t sock){
return psync_wait_socket_writable(sock, PSYNC_SOCK_WRITE_TIMEOUT);
}
static int
psync_wait_socket_readable_microsec(psync_socket_t sock, long sec, long usec){
fd_set rfds;
struct timeval tv;
#if IS_DEBUG
struct timespec start, end;
unsigned long msec;
#endif
int res;
tv.tv_sec=sec;
tv.tv_usec=usec;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
#if IS_DEBUG
psync_nanotime(&start);
#endif
res=select(sock+1, &rfds, NULL, NULL, &tv);
if (res==1){
#if IS_DEBUG
psync_nanotime(&end);
msec=(end.tv_sec-start.tv_sec)*1000+end.tv_nsec/1000000-start.tv_nsec/1000000;
if (msec>=30000)
debug(D_WARNING, "got response from socket after %lu milliseconds", msec);
else if (msec>=5000)
debug(D_NOTICE, "got response from socket after %lu milliseconds", msec);
#endif
return 0;
}
if (res==0){
if (sec)
debug(D_WARNING, "socket read timeouted on %ld seconds", sec);
psync_sock_set_err(P_TIMEDOUT);
}
else
debug(D_WARNING, "select returned %d", res);
return SOCKET_ERROR;
}
#define psync_wait_socket_readable(sock, sec) psync_wait_socket_readable_microsec(sock, sec, 0)
int
psync_wait_socket_read_timeout(psync_socket_t sock){
return psync_wait_socket_readable(sock, PSYNC_SOCK_READ_TIMEOUT);
}
static psync_socket_t
connect_res(struct addrinfo *res){
psync_socket_t sock;
#if defined(SOCK_NONBLOCK)
#if defined(SOCK_CLOEXEC)
#define PSOCK_TYPE_OR (SOCK_NONBLOCK|SOCK_CLOEXEC)
#else
#define PSOCK_TYPE_OR SOCK_NONBLOCK
#endif
#else
#define PSOCK_TYPE_OR 0
#define PSOCK_NEED_NOBLOCK
#endif
while (res){
sock=socket(res->ai_family, res->ai_socktype|PSOCK_TYPE_OR, res->ai_protocol);
if (likely_log(sock!=INVALID_SOCKET)){
#if defined(PSOCK_NEED_NOBLOCK)
fcntl(sock, F_SETFD, FD_CLOEXEC);
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL)|O_NONBLOCK);
#endif
if ((connect(sock, res->ai_addr, res->ai_addrlen)!=SOCKET_ERROR) ||
(psync_sock_err()==P_INPROGRESS && !psync_wait_socket_writable(sock, PSYNC_SOCK_CONNECT_TIMEOUT)))
return sock;
psync_close_socket(sock);
}
res=res->ai_next;
}
return INVALID_SOCKET;
}
psync_socket_t psync_create_socket(int domain, int type, int protocol){
psync_socket_t ret;
ret=socket(domain, type, protocol);
return ret;
}
static void
addr_save_to_db(const char *host, const char *port, struct addrinfo *addr){
psync_sql_res *res;
uint64_t id;
if (psync_sql_isrdlocked()){
if (psync_sql_tryupgradelock())
return;
else
debug(D_NOTICE, "upgraded read to write lock to save data to DB");
}
psync_sql_start_transaction();
res=psync_sql_prep_statement("DELETE FROM resolver WHERE hostname=? AND port=?");
psync_sql_bind_string(res, 1, host);
psync_sql_bind_string(res, 2, port);
psync_sql_run_free(res);
res=psync_sql_prep_statement("INSERT INTO resolver (hostname, port, prio, created, family, socktype, protocol, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
psync_sql_bind_string(res, 1, host);
psync_sql_bind_string(res, 2, port);
psync_sql_bind_uint(res, 4, psync_timer_time());
id=0;
do {
psync_sql_bind_uint(res, 3, id++);
psync_sql_bind_int(res, 5, addr->ai_family);
psync_sql_bind_int(res, 6, addr->ai_socktype);
psync_sql_bind_int(res, 7, addr->ai_protocol);
psync_sql_bind_blob(res, 8, (char *)addr->ai_addr, addr->ai_addrlen);
psync_sql_run(res);
addr=addr->ai_next;
} while (addr);
psync_sql_free_result(res);
psync_sql_commit_transaction();
}
static struct addrinfo *
addr_load_from_db(const char *host, const char *port){
psync_sql_res *res;
psync_uint_row row;
psync_variant_row vrow;
struct addrinfo *ret;
char *data;
const char *str;
uint64_t i;
size_t len;
psync_sql_rdlock();
res=psync_sql_query_nolock("SELECT COUNT(*), SUM(LENGTH(data)) FROM resolver WHERE hostname=? AND port=?");
psync_sql_bind_string(res, 1, host);
psync_sql_bind_string(res, 2, port);
if (!(row=psync_sql_fetch_rowint(res)) || row[0]==0){
psync_sql_free_result(res);
psync_sql_rdunlock();
return NULL;
}
ret=(struct addrinfo *)psync_malloc(sizeof(struct addrinfo)*row[0]+row[1]);
data=(char *)(ret+row[0]);
for (i=0; i<row[0]-1; i++)
ret[i].ai_next=&ret[i+1];
ret[i].ai_next=NULL;
psync_sql_free_result(res);
res=psync_sql_query_nolock("SELECT family, socktype, protocol, data FROM resolver WHERE hostname=? AND port=? ORDER BY prio");
psync_sql_bind_string(res, 1, host);
psync_sql_bind_string(res, 2, port);
i=0;
while ((vrow=psync_sql_fetch_row(res))){
ret[i].ai_family=psync_get_snumber(vrow[0]);
ret[i].ai_socktype=psync_get_snumber(vrow[1]);
ret[i].ai_protocol=psync_get_snumber(vrow[2]);
str=psync_get_lstring(vrow[3], &len);
ret[i].ai_addr=(struct sockaddr *)data;
ret[i].ai_addrlen=len;
i++;
memcpy(data, str, len);
data+=len;
}
psync_sql_free_result(res);
psync_sql_rdunlock();
return ret;
}
static int
addr_still_valid(struct addrinfo *olda, struct addrinfo *newa){
struct addrinfo *a;
do {
a=newa;
while (1){
if (a->ai_addrlen==olda->ai_addrlen && !memcmp(a->ai_addr, olda->ai_addr, a->ai_addrlen))
break;
a=a->ai_next;
if (!a)
return 0;
}
olda=olda->ai_next;
} while (olda);
return 1;
}
typedef struct {
const char *host;
const char *port;
} resolve_host_port;
static void
connect_res_callback(void *h, void *ptr){
struct addrinfo *res;
psync_socket_t sock;
int r;
res=(struct addrinfo *)ptr;
sock=connect_res(res);
r=psync_task_complete(h, (void *)(uintptr_t)sock);
psync_free(res);
if (r && sock!=INVALID_SOCKET)
psync_close_socket(sock);
}
static void
resolve_callback(void *h, void *ptr){
resolve_host_port *hp;
struct addrinfo *res;
struct addrinfo hints;
int rc;
hp=(resolve_host_port *)ptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_STREAM;
res=NULL;
rc=getaddrinfo(hp->host, hp->port, &hints, &res);
if (unlikely(rc!=0))
res=NULL;
psync_task_complete(h, res);
}
#if defined(PSYNC_HAS_PROXY_CODE)
static int
recent_detect(){
static time_t lastdetect=0;
if (psync_timer_time()<lastdetect+60)
return 1;
else{
lastdetect=psync_timer_time();
return 0;
}
}
#endif
static void
detect_proxy(){
// noop, only implemented for windows
}
static psync_socket_t
connect_socket_direct(const char *host, const char *port){
struct addrinfo *res, *dbres;
struct addrinfo hints;
psync_socket_t sock;
int rc;
debug(D_NOTICE, "connecting to %s:%s", host, port);
dbres=addr_load_from_db(host, port);
if (dbres){
resolve_host_port resolv;
void *params[2];
psync_task_callback_t callbacks[2];
psync_task_manager_t tasks;
resolv.host=host;
resolv.port=port;
params[0]=dbres;
params[1]=&resolv;
callbacks[0]=connect_res_callback;
callbacks[1]=resolve_callback;
tasks=psync_task_run_tasks(callbacks, params, 2);
res=(struct addrinfo *)psync_task_get_result(tasks, 1);
if (unlikely(!res)){
psync_task_free(tasks);
detect_proxy();
debug(D_WARNING, "failed to resolve %s", host);
return INVALID_SOCKET;
}
addr_save_to_db(host, port, res);
if (addr_still_valid(dbres, res)){
debug(D_NOTICE, "successfully reused cached IP for %s:%s", host, port);
sock=(psync_socket_t)(uintptr_t)psync_task_get_result(tasks, 0);
}
else{
debug(D_NOTICE, "cached IP not valid for %s:%s", host, port);
sock=connect_res(res);
}
freeaddrinfo(res);
psync_task_free(tasks);
}
else{
memset(&hints, 0, sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_STREAM;
res=NULL;
rc=getaddrinfo(host, port, &hints, &res);
if (unlikely(rc!=0)){
debug(D_WARNING, "failed to resolve %s", host);
detect_proxy();
return INVALID_SOCKET;
}
addr_save_to_db(host, port, res);
sock=connect_res(res);
freeaddrinfo(res);
}
if (likely(sock!=INVALID_SOCKET)){
int sock_opt=1;
#if defined(P_OS_LINUX)
setsockopt(sock, SOL_TCP, TCP_NODELAY, (char *)&sock_opt, sizeof(sock_opt));
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&sock_opt, sizeof(sock_opt));
#endif
#if defined(SOL_TCP)
#if defined(TCP_KEEPCNT)
sock_opt=3;
setsockopt(sock, SOL_TCP, TCP_KEEPCNT, (char *)&sock_opt, sizeof(sock_opt));
#endif
#if defined(TCP_KEEPIDLE)
sock_opt=60;
setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, (char *)&sock_opt, sizeof(sock_opt));
#endif
#if defined(TCP_KEEPINTVL)
sock_opt=20;
setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, (char *)&sock_opt, sizeof(sock_opt));
#endif
#endif
}
else{
detect_proxy();
debug(D_WARNING, "failed to connect to %s:%s", host, port);
}
return sock;
}
static int check_http_resp(char *str) {
if (memcmp(str, "HTTP", 4)){
debug(D_WARNING, "bad proxy response %s", str);
return 0;
}
while (*str && !isspace(*str))
str++;
while (*str && isspace(*str))
str++;
if (!isdigit(*str)){
debug(D_WARNING, "bad proxy response %s", str);
return 0;
}
if (atoi(str)!=200) {
debug(D_NOTICE, "proxy returned HTTP code %d", atoi(str));
return 0;
}
return 1;
}
static psync_socket_t
connect_socket_connect_proxy(const char *host, const char *port){
char buff[2048], *str;
psync_socket_t sock;
int ln, wr, r, rc;
sock=connect_socket_direct(proxy_host, proxy_port);
if (unlikely(sock==INVALID_SOCKET)){
debug(D_NOTICE, "connection to proxy %s:%s failed", proxy_host, proxy_port);
goto err0;
}
ln=psync_slprintf(buff, sizeof(buff), "CONNECT %s:%s HTTP/1.0\015\012User-Agent: %s\015\012\015\012", host, port, psync_software_name);
wr=0;
while (wr<ln){
r=psync_write_socket(sock, buff+wr, ln-wr);
if (unlikely(r==SOCKET_ERROR)){
if (likely_log((psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR) && !psync_wait_socket_write_timeout(sock)))
continue;
else
goto err1;
}
wr+=r;
}
wr=0;
rc=0;
while (1){
if (unlikely(psync_wait_socket_read_timeout(sock))){
debug(D_WARNING, "connection to %s:%s via %s:%s timeouted", host, port, proxy_host, proxy_port);
goto err1;
}
r=psync_read_socket(sock, buff+wr, sizeof(buff)-1-wr);
if (unlikely(r==0 || r==SOCKET_ERROR)){
if (r==0){
debug(D_NOTICE, "proxy server %s:%s closed connection", proxy_host, proxy_port);
goto err1;
}
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR))
continue;
else
goto err1;
}
wr+=r;
buff[wr]=0;
str=strstr(buff, "\015\012\015\012");
if (str){
if (rc || check_http_resp(buff)){
debug(D_NOTICE, "connected to %s:%s via %s:%s", host, port, proxy_host, proxy_port);
return sock;
}else
goto err1;
}
if (wr==sizeof(buff)-1){
rc=check_http_resp(buff);
if (!rc)
goto err1;
memcpy(buff, buff+sizeof(buff)-8, 8);
wr=7; // yes, 7
}
}
err1:
psync_close_socket(sock);
err0:
detect_proxy();
if (proxy_type!=PROXY_CONNECT)
return connect_socket_direct(host, port);
else
return INVALID_SOCKET;
}
static psync_socket_t
connect_socket(const char *host, const char *port){
if (unlikely(!proxy_detected)){
proxy_detected=1;
detect_proxy();
}
if (likely(proxy_type!=PROXY_CONNECT))
return connect_socket_direct(host, port);
else
return connect_socket_connect_proxy(host, port);
}
static int
wait_sock_ready_for_ssl(psync_socket_t sock){
fd_set fds, *rfds, *wfds;
struct timeval tv;
int res;
FD_ZERO(&fds);
FD_SET(sock, &fds);
if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ){
rfds=&fds;
wfds=NULL;
tv.tv_sec=PSYNC_SOCK_READ_TIMEOUT;
}
else if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE){
rfds=NULL;
wfds=&fds;
tv.tv_sec=PSYNC_SOCK_WRITE_TIMEOUT;
}
else{
debug(D_BUG, "this functions should only be called when SSL returns WANT_READ/WANT_WRITE");
psync_sock_set_err(P_INVAL);
return SOCKET_ERROR;
}
tv.tv_usec=0;
res=select(sock+1, rfds, wfds, NULL, &tv);
if (res==1)
return 0;
if (res==0){
debug(D_WARNING, "socket timeouted");
psync_sock_set_err(P_TIMEDOUT);
}
return PRINT_RETURN_CONST(SOCKET_ERROR);
}
psync_socket *
psync_socket_connect(const char *host, int unsigned port, int ssl){
psync_socket *ret;
void *sslc;
psync_socket_t sock;
char sport[8];
psync_slprintf(sport, sizeof(sport), "%d", port);
sock=connect_socket(host, sport);
if (unlikely_log(sock==INVALID_SOCKET))
return NULL;
if (ssl){
ssl=psync_ssl_connect(sock, &sslc, host);
while (ssl==PSYNC_SSL_NEED_FINISH){
if (wait_sock_ready_for_ssl(sock)){
psync_ssl_free(sslc);
break;
}
ssl=psync_ssl_connect_finish(sslc, host);
}
if (unlikely_log(ssl!=PSYNC_SSL_SUCCESS)){
psync_close_socket(sock);
return NULL;
}
}
else
sslc=NULL;
ret=psync_new(psync_socket);
ret->ssl=sslc;
ret->buffer=NULL;
ret->sock=sock;
ret->pending=0;
return ret;
}
void
psync_socket_close(psync_socket *sock){
if (sock->ssl)
while (psync_ssl_shutdown(sock->ssl)==PSYNC_SSL_NEED_FINISH)
if (wait_sock_ready_for_ssl(sock->sock)){
psync_ssl_free(sock->ssl);
break;
}
psync_socket_clear_write_buffered(sock);
psync_close_socket(sock->sock);
psync_free(sock);
}
void
psync_socket_close_bad(psync_socket *sock){
if (sock->ssl)
psync_ssl_free(sock->ssl);
psync_socket_clear_write_buffered(sock);
psync_close_socket(sock->sock);
psync_free(sock);
}
void
psync_socket_set_write_buffered(psync_socket *sock){
psync_socket_buffer *sb;
if (sock->buffer)
return;
sb=(psync_socket_buffer *)psync_malloc(offsetof(psync_socket_buffer, buff)+PSYNC_FIRST_SOCK_WRITE_BUFF_SIZE);
sb->next=NULL;
sb->size=PSYNC_FIRST_SOCK_WRITE_BUFF_SIZE;
sb->woffset=0;
sb->roffset=0;
sock->buffer=sb;
}
void
psync_socket_set_write_buffered_thread(psync_socket *sock){
pthread_mutex_lock(&socket_mutex);
psync_socket_set_write_buffered(sock);
pthread_mutex_unlock(&socket_mutex);
}
void
psync_socket_clear_write_buffered(psync_socket *sock){
psync_socket_buffer *nb;
while (sock->buffer){
nb=sock->buffer->next;
free(sock->buffer);
sock->buffer=nb;
}
}
void
psync_socket_clear_write_buffered_thread(psync_socket *sock){
pthread_mutex_lock(&socket_mutex);
psync_socket_clear_write_buffered(sock);
pthread_mutex_unlock(&socket_mutex);
}
int
psync_socket_set_recvbuf(psync_socket *sock, int bufsize){
#if defined(SO_RCVBUF) && defined(SOL_SOCKET)
return setsockopt(sock->sock, SOL_SOCKET, SO_RCVBUF, (const char*)&bufsize, sizeof(bufsize));
#else
return -1;
#endif
}
int
psync_socket_set_sendbuf(psync_socket *sock, int bufsize){
#if defined(SO_SNDBUF) && defined(SOL_SOCKET)
return setsockopt(sock->sock, SOL_SOCKET, SO_SNDBUF, (const char*)&bufsize, sizeof(bufsize));
#else
return -1;
#endif
}
int
psync_socket_isssl(psync_socket *sock){
if (sock->ssl)
return 1;
else
return 0;
}
int
psync_socket_pendingdata(psync_socket *sock){
if (sock->pending)
return 1;
if (sock->ssl)
return psync_ssl_pendingdata(sock->ssl);
else
return 0;
}
int
psync_socket_pendingdata_buf(psync_socket *sock){
int ret;
#if defined(FIONREAD)
if (ioctl(sock->sock, FIONREAD, &ret))
return -1;
#else
return -1;
#endif
if (sock->ssl)
ret+=psync_ssl_pendingdata(sock->ssl);
return ret;
}
int
psync_socket_pendingdata_buf_thread(psync_socket *sock){
int ret;
pthread_mutex_lock(&socket_mutex);
ret=psync_socket_pendingdata_buf(sock);
pthread_mutex_unlock(&socket_mutex);
return ret;
}
int
psync_socket_try_write_buffer(psync_socket *sock){
if (sock->buffer){
psync_socket_buffer *b;
int wrt, cw;
wrt=0;
while ((b=sock->buffer)){
if (b->roffset==b->woffset){
sock->buffer=b->next;
psync_free(b);
continue;
}
if (sock->ssl){
cw=psync_ssl_write(sock->ssl, b->buff+b->roffset, b->woffset-b->roffset);
if (cw==PSYNC_SSL_FAIL){
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE))
break;
else{
if (!wrt)
wrt=-1;
break;
}
}
}
else{
cw=psync_write_socket(sock->sock, b->buff+b->roffset, b->woffset-b->roffset);
if (cw==SOCKET_ERROR){
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR))
break;
else{
if (!wrt)
wrt=-1;
break;
}
}
}
wrt+=cw;
b->roffset+=cw;
if (b->roffset!=b->woffset)
break;
}
if (wrt>0)
debug(D_NOTICE, "wrote %d bytes to socket from buffers", wrt);
return wrt;
}
else
return 0;
}
int
psync_socket_try_write_buffer_thread(psync_socket *sock){
int ret;
pthread_mutex_lock(&socket_mutex);
ret=psync_socket_try_write_buffer(sock);
pthread_mutex_unlock(&socket_mutex);
return ret;
}
int
psync_socket_readable(psync_socket *sock){
psync_socket_try_write_buffer(sock);
if (sock->ssl && psync_ssl_pendingdata(sock->ssl))
return 1;
else if (psync_wait_socket_readable(sock->sock, 0))
return 0;
else{
sock->pending=1;
return 1;
}
}
int
psync_socket_writable(psync_socket *sock){
if (sock->buffer)
return 1;
return !psync_wait_socket_writable(sock->sock, 0);
}
static int
psync_socket_read_ssl(psync_socket *sock, void *buff, int num){
int r;
psync_socket_try_write_buffer(sock);
if (!psync_ssl_pendingdata(sock->ssl) && !sock->pending && psync_wait_socket_read_timeout(sock->sock))
return -1;
sock->pending=0;
while (1){
psync_socket_try_write_buffer(sock);
r=psync_ssl_read(sock->ssl, buff, num);
if (r==PSYNC_SSL_FAIL){
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
if (wait_sock_ready_for_ssl(sock->sock)){
if (sock->buffer)
debug(D_WARNING, "timeouted on socket with pending buffers");
return -1;
}
else
continue;
}
else{
psync_sock_set_err(P_CONNRESET);
return -1;
}
}
else
return r;
}
}
static int
psync_socket_read_plain(psync_socket *sock, void *buff, int num){
int r;
while (1){
psync_socket_try_write_buffer(sock);
if (sock->pending)
sock->pending=0;
else if (psync_wait_socket_read_timeout(sock->sock)){
debug(D_WARNING, "timeouted on socket with pending buffers");
return -1;
}
else
psync_socket_try_write_buffer(sock);
r=psync_read_socket(sock->sock, buff, num);
if (r==SOCKET_ERROR){
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
continue;
else
return -1;
}
else
return r;
}
}
int
psync_socket_read(psync_socket *sock, void *buff, int num){
if (sock->ssl)
return psync_socket_read_ssl(sock, buff, num);
else
return psync_socket_read_plain(sock, buff, num);
}
static int
psync_socket_read_noblock_ssl(psync_socket *sock, void *buff, int num){
int r;
r=psync_ssl_read(sock->ssl, buff, num);
if (r==PSYNC_SSL_FAIL){
sock->pending=0;
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE))
return PSYNC_SOCKET_WOULDBLOCK;
else{
psync_sock_set_err(P_CONNRESET);
return -1;
}
}
else
return r;
}
static int
psync_socket_read_noblock_plain(psync_socket *sock, void *buff, int num){
int r;
r=psync_read_socket(sock->sock, buff, num);
if (r==SOCKET_ERROR){
sock->pending=0;
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
return PSYNC_SOCKET_WOULDBLOCK;
else
return -1;
}
else
return r;
}
int
psync_socket_read_noblock(psync_socket *sock, void *buff, int num){
psync_socket_try_write_buffer(sock);
if (sock->ssl)
return psync_socket_read_noblock_ssl(sock, buff, num);
else
return psync_socket_read_noblock_plain(sock, buff, num);
}
static int
psync_socket_read_ssl_thread(psync_socket *sock, void *buff, int num){
int r;
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
pthread_mutex_unlock(&socket_mutex);
if (!psync_ssl_pendingdata(sock->ssl) && !sock->pending && psync_wait_socket_read_timeout(sock->sock))
return -1;
sock->pending=0;
while (1){
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
r=psync_ssl_read(sock->ssl, buff, num);
pthread_mutex_unlock(&socket_mutex);
if (r==PSYNC_SSL_FAIL){
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
if (wait_sock_ready_for_ssl(sock->sock))
return -1;
else
continue;
}
else{
psync_sock_set_err(P_CONNRESET);
return -1;
}
}
else
return r;
}
}
static int
psync_socket_read_plain_thread(psync_socket *sock, void *buff, int num){
int r;
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
pthread_mutex_unlock(&socket_mutex);
while (1){
if (sock->pending)
sock->pending=0;
else if (psync_wait_socket_read_timeout(sock->sock))
return -1;
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
r=psync_read_socket(sock->sock, buff, num);
pthread_mutex_unlock(&socket_mutex);
if (r==SOCKET_ERROR){
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
continue;
else
return -1;
}
else
return r;
}
}
int
psync_socket_read_thread(psync_socket *sock, void *buff, int num){
if (sock->ssl)
return psync_socket_read_ssl_thread(sock, buff, num);
else
return psync_socket_read_plain_thread(sock, buff, num);
}
static int
psync_socket_write_to_buf(psync_socket *sock, const void *buff, int num){
psync_socket_buffer *b;
assert(sock->buffer);
b=sock->buffer;
while (b->next)
b=b->next;
if (likely(b->size-b->woffset>=num)){
memcpy(b->buff+b->woffset, buff, num);
b->woffset+=num;
return num;
}
else{
uint32_t rnum, wr;
rnum=num;
do {
wr=b->size-b->woffset;
if (!wr){
b->next=(psync_socket_buffer *)psync_malloc(offsetof(psync_socket_buffer, buff)+PSYNC_SECOND_SOCK_WRITE_BUFF_SIZE);
b=b->next;
b->next=NULL;
b->size=PSYNC_SECOND_SOCK_WRITE_BUFF_SIZE;
b->woffset=0;
b->roffset=0;
wr=PSYNC_SECOND_SOCK_WRITE_BUFF_SIZE;
}
if (wr>rnum)
wr=rnum;
memcpy(b->buff+b->woffset, buff, wr);
b->woffset+=wr;
buff=(const char *)buff+wr;
rnum-=wr;
} while (rnum);
return num;
}
}
int
psync_socket_write(psync_socket *sock, const void *buff, int num){
int r;
if (sock->buffer)
return psync_socket_write_to_buf(sock, buff, num);
if (psync_wait_socket_write_timeout(sock->sock))
return -1;
if (sock->ssl){
r=psync_ssl_write(sock->ssl, buff, num);
if (r==PSYNC_SSL_FAIL){
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE))
return 0;
else
return -1;
}
}
else{
r=psync_write_socket(sock->sock, buff, num);
if (r==SOCKET_ERROR){
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN || psync_sock_err()==P_INTR))
return 0;
else
return -1;
}
}
return r;
}
static int
psync_socket_readall_ssl(psync_socket *sock, void *buff, int num){
int br, r;
br=0;
psync_socket_try_write_buffer(sock);
if (!psync_ssl_pendingdata(sock->ssl) && !sock->pending && psync_wait_socket_read_timeout(sock->sock)) {
return -1;
}
sock->pending=0;
while (br<num){
psync_socket_try_write_buffer(sock);
r=psync_ssl_read(sock->ssl, (char *)buff+br, num-br);
if (r==PSYNC_SSL_FAIL){
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
if (wait_sock_ready_for_ssl(sock->sock))
return -1;
else
continue;
}
else{
psync_sock_set_err(P_CONNRESET);
return -1;
}
}
if (r==0)
return br;
br+=r;
}
return br;
}
static int
psync_socket_readall_plain(psync_socket *sock, void *buff, int num){
int br, r;
br=0;
while (br<num){
psync_socket_try_write_buffer(sock);
if (sock->pending)
sock->pending=0;
else if (psync_wait_socket_read_timeout(sock->sock))
return -1;
else
psync_socket_try_write_buffer(sock);
r=psync_read_socket(sock->sock, (char *)buff+br, num-br);
if (r==SOCKET_ERROR){
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
continue;
else
return -1;
}
if (r==0)
return br;
br+=r;
}
return br;
}
int
psync_socket_readall(psync_socket* sock, void* buff, int num) {
if (sock->ssl) {
return psync_socket_readall_ssl(sock, buff, num);
}
else {
return psync_socket_readall_plain(sock, buff, num);
}
}
static int
psync_socket_writeall_ssl(psync_socket *sock, const void *buff, int num){
int br, r;
br=0;
while (br<num){
r=psync_ssl_write(sock->ssl, (char *)buff+br, num-br);
if (r==PSYNC_SSL_FAIL){
if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE){
if (wait_sock_ready_for_ssl(sock->sock))
return -1;
else
continue;
}
else{
psync_sock_set_err(P_CONNRESET);
return -1;
}
}
if (r==0)
return br;
br+=r;
}
return br;
}
static int
psync_socket_writeall_plain(psync_socket_t sock, const void *buff, int num){
int br, r;
br=0;
while (br<num){
r=psync_write_socket(sock, (const char *)buff+br, num-br);
if (r==SOCKET_ERROR){
if (psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN){
if (psync_wait_socket_write_timeout(sock))
return -1;
else
continue;
}
else
return -1;
}
br+=r;
}
return br;
}
int
psync_socket_writeall(psync_socket *sock, const void *buff, int num){
if (sock->buffer)
return psync_socket_write_to_buf(sock, buff, num);
if (sock->ssl)
return psync_socket_writeall_ssl(sock, buff, num);
else
return psync_socket_writeall_plain(sock->sock, buff, num);
}
static int
psync_socket_readall_ssl_thread(psync_socket *sock, void *buff, int num){
int br, r;
br=0;
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
r=psync_ssl_pendingdata(sock->ssl);
pthread_mutex_unlock(&socket_mutex);
if (!r && !sock->pending && psync_wait_socket_read_timeout(sock->sock))
return -1;
sock->pending=0;
while (br<num){
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
r=psync_ssl_read(sock->ssl, (char *)buff+br, num-br);
pthread_mutex_unlock(&socket_mutex);
if (r==PSYNC_SSL_FAIL){
if (likely_log(psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE)){
if (wait_sock_ready_for_ssl(sock->sock))
return -1;
else
continue;
}
else{
psync_sock_set_err(P_CONNRESET);
return -1;
}
}
if (r==0)
return br;
br+=r;
}
return br;
}
static int
psync_socket_readall_plain_thread(psync_socket *sock, void *buff, int num){
int br, r;
br=0;
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
pthread_mutex_unlock(&socket_mutex);
while (br<num){
if (sock->pending)
sock->pending=0;
else if (psync_wait_socket_read_timeout(sock->sock))
return -1;
pthread_mutex_lock(&socket_mutex);
psync_socket_try_write_buffer(sock);
r=psync_read_socket(sock->sock, (char *)buff+br, num-br);
pthread_mutex_unlock(&socket_mutex);
if (r==SOCKET_ERROR){
if (likely_log(psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN))
continue;
else
return -1;
}
if (r==0)
return br;
br+=r;
}
return br;
}
int
psync_socket_readall_thread(psync_socket *sock, void *buff, int num){
if (sock->ssl)
return psync_socket_readall_ssl_thread(sock, buff, num);
else
return psync_socket_readall_plain_thread(sock, buff, num);
}
static int
psync_socket_writeall_ssl_thread(psync_socket *sock, const void *buff, int num){
int br, r;
br=0;
while (br<num){
pthread_mutex_lock(&socket_mutex);
if (sock->buffer)
r=psync_socket_write_to_buf(sock, buff, num);
else
r=psync_ssl_write(sock->ssl, (char *)buff+br, num-br);
pthread_mutex_unlock(&socket_mutex);
if (r==PSYNC_SSL_FAIL){
if (psync_ssl_errno==PSYNC_SSL_ERR_WANT_READ || psync_ssl_errno==PSYNC_SSL_ERR_WANT_WRITE){
if (wait_sock_ready_for_ssl(sock->sock))
return -1;
else
continue;
}
else{
psync_sock_set_err(P_CONNRESET);
return -1;
}
}
if (r==0)
return br;
br+=r;
}
return br;
}
static int
psync_socket_writeall_plain_thread(psync_socket *sock, const void *buff, int num){
int br, r;
br=0;
while (br<num){
pthread_mutex_lock(&socket_mutex);
if (sock->buffer)
r=psync_socket_write_to_buf(sock, buff, num);
else
r=psync_write_socket(sock->sock, (const char *)buff+br, num-br);
pthread_mutex_unlock(&socket_mutex);
if (r==SOCKET_ERROR){
if (psync_sock_err()==P_WOULDBLOCK || psync_sock_err()==P_AGAIN){
if (psync_wait_socket_write_timeout(sock->sock))
return -1;
else
continue;
}
else
return -1;
}
br+=r;
}
return br;
}
int
psync_socket_writeall_thread(psync_socket *sock, const void *buff, int num){
if (sock->ssl)
return psync_socket_writeall_ssl_thread(sock, buff, num);
else
return psync_socket_writeall_plain_thread(sock, buff, num);
}
static void
copy_address(struct sockaddr_storage *dst, const struct sockaddr *src) {
dst->ss_family=src->sa_family;
if (src->sa_family==AF_INET)
memcpy(&((struct sockaddr_in *)dst)->sin_addr, &((const struct sockaddr_in *)src)->sin_addr, sizeof(((struct sockaddr_in *)dst)->sin_addr));
else
memcpy(&((struct sockaddr_in6 *)dst)->sin6_addr, &((const struct sockaddr_in6 *)src)->sin6_addr, sizeof(((struct sockaddr_in6 *)dst)->sin6_addr));
}
psync_interface_list_t *
psync_list_ip_adapters(){
psync_interface_list_t *ret;
size_t cnt;
struct ifaddrs *addrs, *addr;
sa_family_t family;
size_t sz;
if (unlikely_log(getifaddrs(&addrs)))
goto empty;
cnt=0;
addr=addrs;
while (addr){
if (addr->ifa_addr){
family=addr->ifa_addr->sa_family;
if ((family==AF_INET || family==AF_INET6) && addr->ifa_broadaddr && addr->ifa_netmask)
cnt++;
}
addr=addr->ifa_next;
}
ret=psync_malloc(offsetof(psync_interface_list_t, interfaces)+sizeof(psync_interface_t)*cnt);
memset(ret, 0, offsetof(psync_interface_list_t, interfaces)+sizeof(psync_interface_t)*cnt);
ret->interfacecnt=cnt;
addr=addrs;
cnt=0;
while (addr){
if (addr->ifa_addr){
family=addr->ifa_addr->sa_family;
if ((family==AF_INET || family==AF_INET6) && addr->ifa_broadaddr && addr->ifa_netmask){
if (family==AF_INET)
sz=sizeof(struct sockaddr_in);
else
sz=sizeof(struct sockaddr_in6);
copy_address(&ret->interfaces[cnt].address, addr->ifa_addr);
copy_address(&ret->interfaces[cnt].broadcast, addr->ifa_broadaddr);
copy_address(&ret->interfaces[cnt].netmask, addr->ifa_netmask);
ret->interfaces[cnt].addrsize=sz;
cnt++;
}
}
addr=addr->ifa_next;
}
freeifaddrs(addrs);
return ret;
empty:
ret=psync_malloc(offsetof(psync_interface_list_t, interfaces));
ret->interfacecnt=0;
return ret;
}
int
psync_pipe(psync_socket_t pipefd[2]){
return pipe(pipefd);
}
int
psync_pipe_close(psync_socket_t pfd){
return psync_close_socket(pfd);
}
int
psync_pipe_read(psync_socket_t pfd, void *buff, int num){
return read(pfd, buff, num);
}
int
psync_pipe_write(psync_socket_t pfd, const void *buff, int num){
return write(pfd, buff, num);
}
int
psync_socket_pair(psync_socket_t sfd[2]){
return socketpair(AF_UNIX, SOCK_STREAM, 0, sfd);
}
int
psync_socket_is_broken(psync_socket_t sock){
fd_set rfds;
struct timeval tv;
memset(&tv, 0, sizeof(tv));
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
return select(sock+1, NULL, NULL, &rfds, &tv)==1;
}
int
psync_select_in(psync_socket_t *sockets, int cnt, int64_t timeoutmillisec){
fd_set rfds;
struct timeval tv, *ptv;
psync_socket_t max;
int i;
if (timeoutmillisec<0)
ptv=NULL;
else{
tv.tv_sec=timeoutmillisec/1000;
tv.tv_usec=(timeoutmillisec%1000)*1000;
ptv=&tv;
}
FD_ZERO(&rfds);
max=0;
for (i=0; i<cnt; i++){
FD_SET(sockets[i], &rfds);
if (sockets[i]>=max)
max=sockets[i]+1;
}
i=select(max, &rfds, NULL, NULL, ptv);
if (i>0){
for (i=0; i<cnt; i++)
if (FD_ISSET(sockets[i], &rfds))
return i;
}
else if (i==0)
psync_sock_set_err(P_TIMEDOUT);
return SOCKET_ERROR;
}
int
psync_list_dir(const char *path, psync_list_dir_callback callback, void *ptr){
psync_pstat pst;
DIR *dh;
char *cpath;
size_t pl, entrylen;
long namelen;
struct dirent *entry; // XXX: still useful?
struct dirent *de;
dh=opendir(path);
if (unlikely(!dh)){
debug(D_WARNING, "could not open directory %s", path);
goto err1;
}
pl=strlen(path);
namelen=pathconf(path, _PC_NAME_MAX);
if (unlikely_log(namelen==-1))
namelen=255;
if (namelen<sizeof(de->d_name)-1)
namelen=sizeof(de->d_name)-1;
entrylen=offsetof(struct dirent, d_name)+namelen+1;
cpath=(char *)psync_malloc(pl+namelen+2);
entry=(struct dirent *)psync_malloc(entrylen);
memcpy(cpath, path, pl);
if (!pl || cpath[pl-1]!=PSYNC_DIRECTORY_SEPARATORC)
cpath[pl++]=PSYNC_DIRECTORY_SEPARATORC;
pst.path=cpath;
//while (!readdir_r(dh, entry, &de) && de) {// DELETEME: deprecated
while((de = readdir(dh))) {
if (de->d_name[0]!='.' || (de->d_name[1]!=0 && (de->d_name[1]!='.' || de->d_name[2]!=0))){
psync_strlcpy(cpath+pl, de->d_name, namelen+1);
if (likely_log(!lstat(cpath, &pst.stat)) && (S_ISREG(pst.stat.st_mode) || S_ISDIR(pst.stat.st_mode))){
pst.name=de->d_name;
callback(ptr, &pst);
}
}
}
psync_free(entry);
psync_free(cpath);
closedir(dh);
return 0;
err1:
psync_error=PERROR_LOCAL_FOLDER_NOT_FOUND;
return -1;
}
int
psync_list_dir_fast(const char *path, psync_list_dir_callback_fast callback, void *ptr){
psync_pstat_fast pst;
struct stat st;
DIR *dh;
char *cpath;
size_t pl, entrylen;
long namelen;
struct dirent *entry; // XXX: still useful?
struct dirent *de;
dh=opendir(path);
if (unlikely_log(!dh))
goto err1;
pl=strlen(path);
namelen=pathconf(path, _PC_NAME_MAX);
if (namelen==-1)
namelen=255;
if (namelen<sizeof(de->d_name)-1)
namelen=sizeof(de->d_name)-1;
entrylen=offsetof(struct dirent, d_name)+namelen+1;
cpath=(char *)psync_malloc(pl+namelen+2);
entry=(struct dirent *)psync_malloc(entrylen);
memcpy(cpath, path, pl);
if (!pl || cpath[pl-1]!=PSYNC_DIRECTORY_SEPARATORC)
cpath[pl++]=PSYNC_DIRECTORY_SEPARATORC;
//while (!readdir_r(dh, entry, &de) && de) { // DELETEME: deprecated
while((de=readdir(dh))) {
if (de->d_name[0]!='.' || (de->d_name[1]!=0 && (de->d_name[1]!='.' || de->d_name[2]!=0))){
#if defined(DT_UNKNOWN) && defined(DT_DIR) && defined(DT_REG)
pst.name=de->d_name;
if (de->d_type==DT_UNKNOWN){
psync_strlcpy(cpath+pl, de->d_name, namelen+1);
if (unlikely_log(lstat(cpath, &st)))
continue;
pst.isfolder=S_ISDIR(st.st_mode);
}
else if (de->d_type==DT_DIR)
pst.isfolder=1;
else if (de->d_type==DT_REG)
pst.isfolder=0;
else
continue;
callback(ptr, &pst);
#else
psync_strlcpy(cpath+pl, de->d_name, namelen+1);
if (likely_log(!lstat(cpath, &st))){
pst.name=de->d_name;
pst.isfolder=S_ISDIR(st.st_mode);
callback(ptr, &pst);
}
#endif
}
}
psync_free(entry);
psync_free(cpath);
closedir(dh);
return 0;
err1:
psync_error=PERROR_LOCAL_FOLDER_NOT_FOUND;
return -1;
}
int64_t
psync_get_free_space_by_path(const char *path){
struct statvfs buf;
if (unlikely_log(statvfs(path, &buf)))
return -1;
else
return (int64_t)buf.f_bavail*(int64_t)buf.f_frsize;
}
int
psync_mkdir(const char *path){
return mkdir(path, PSYNC_DEFAULT_POSIX_FOLDER_MODE);
}
int
psync_rmdir(const char *path){
return rmdir(path);
}
int
psync_file_rename(const char *oldpath, const char *newpath){
return rename(oldpath, newpath);
}
int
psync_file_rename_overwrite(const char *oldpath, const char *newpath){
if (!psync_filename_cmp(oldpath, newpath))
return 0;
return rename(oldpath, newpath);
}
int
psync_file_delete(const char *path){
return unlink(path);
}
psync_file_t
psync_file_open(const char *path, int access, int flags){
int fd;
#if defined(O_CLOEXEC)
flags|=O_CLOEXEC;
#endif
#if defined(O_NOATIME)
flags|=O_NOATIME;
#endif
fd=open(path, access|flags, PSYNC_DEFAULT_POSIX_FILE_MODE);
if (unlikely(fd==-1)){
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while opening file");
fd=open(path, access|flags, PSYNC_DEFAULT_POSIX_FILE_MODE);
if (fd!=-1)
return fd;
}
}
return fd;
}
int
psync_file_close(psync_file_t fd){
return close(fd);
}
int
psync_file_sync(psync_file_t fd){
#if defined(F_FULLFSYNC)
if (unlikely(fcntl(fd, F_FULLFSYNC))){
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while fsyncing file");
if (!fcntl(fd, F_FULLFSYNC))
return 0;
}
debug(D_NOTICE, "got error %d, when doing fcntl(F_FULLFSYNC), trying fsync()", (int)errno);
if (fsync(fd)){
debug(D_NOTICE, "fsync also failed, error %d", (int)errno);
return -1;
}
else{
debug(D_NOTICE, "fsync succeded");
return 0;
}
}
else
return 0;
#else
#if _POSIX_SYNCHRONIZED_IO>0
if (unlikely(fdatasync(fd))){
#else
if (unlikely(fsync(fd))){
#endif
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while fsyncing file");
if (!fsync(fd))
return 0;
}
debug(D_NOTICE, "got error %d", (int)errno);
return -1;
}
else
return 0;
#endif
}
int
psync_file_schedulesync(psync_file_t fd){
#if defined(P_OS_LINUX) && defined(SYNC_FILE_RANGE_WRITE)
return sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE);
#elif _POSIX_MAPPED_FILES>0 && _POSIX_SYNCHRONIZED_IO>0
struct stat st;
void *fmap;
int ret;
if (unlikely(fstat(fd, &st))){
debug(D_NOTICE, "fstat failed, errno=%d", (int)errno);
return -1;
}
fmap=mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (unlikely(fmap==MAP_FAILED)){
debug(D_NOTICE, "mmap failed, errno=%d", (int)errno);
return -1;
}
ret=msync(fmap, st.st_size, MS_ASYNC);
if (unlikely(ret))
debug(D_NOTICE, "msync failed, errno=%d", (int)errno);
munmap(fmap, st.st_size);
return ret;
#else
return 0;
#endif
}
int
psync_folder_sync(const char *path){
int fd, ret;
fd=open(path, O_RDONLY);
if (fd==-1){
debug(D_NOTICE, "could not open folder %s, error %d", path, (int)errno);
return -1;
}
if (unlikely(psync_file_sync(fd))){
debug(D_NOTICE, "could not fsync folder %s, error %d", path, (int)errno);
ret=-1;
}
else
ret=0;
close(fd);
return ret;
}
psync_file_t
psync_file_dup(psync_file_t fd){
return dup(fd);
}
int
psync_file_set_creation(psync_file_t fd, time_t ctime){
return -1;
}
int
psync_set_crtime_mtime(const char *path, time_t crtime, time_t mtime){
if (mtime){
struct timeval tm[2];
tm[0].tv_sec=mtime;
tm[0].tv_usec=0;
tm[1].tv_sec=mtime;
tm[1].tv_usec=0;
if (unlikely(utimes(path, tm))){
debug(D_NOTICE, "got errno %d while setting modification time of %s to %lu: %s", errno, path, (unsigned long)mtime, strerror(errno));
return -1;
}
else
return 0;
}
else
return 0;
}
int
psync_set_crtime_mtime_by_fd(psync_file_t fd, const char *path, time_t crtime, time_t mtime){
return psync_set_crtime_mtime(path, crtime, mtime);
}
typedef struct {
uint64_t offset;
size_t count;
psync_file_t fd;
} psync_file_preread_t;
static void
psync_file_preread_thread(void *ptr){
char buff[16*1024];
psync_file_preread_t *pr;
ssize_t rd;
pr=(psync_file_preread_t *)ptr;
while (pr->count){
rd=psync_file_pread(pr->fd, buff, pr->count>sizeof(buff)?sizeof(buff):pr->count, pr->offset);
if (rd<=0)
break;
pr->offset+=rd;
pr->count-=rd;
}
psync_file_close(pr->fd);
psync_free(pr);
}
int
psync_file_preread(psync_file_t fd, uint64_t offset, size_t count){
psync_file_preread_t *pr;
psync_file_t cfd;
cfd=psync_file_dup(fd);
if (cfd==INVALID_HANDLE_VALUE)
return -1;
pr=psync_new(psync_file_preread_t);
pr->offset=offset;
pr->count=count;
pr->fd=cfd;
psync_run_thread1("pre-read (readahead) thread", psync_file_preread_thread, pr);
return 0;
}
int
psync_file_readahead(psync_file_t fd, uint64_t offset, size_t count){
#if defined(POSIX_FADV_WILLNEED)
return posix_fadvise(fd, offset, count, POSIX_FADV_WILLNEED);
#elif defined(F_RDADVISE)
struct radvisory ra;
ra.ra_offset=offset;
ra.ra_count=count;
return fcntl(fd, F_RDADVISE, &ra);
#endif
}
ssize_t
psync_file_read(psync_file_t fd, void *buf, size_t count){
ssize_t ret;
ret=read(fd, buf, count);
if (unlikely(ret==-1)){
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while reading from file");
ret=read(fd, buf, count);
if (ret!=-1)
return ret;
}
debug(D_NOTICE, "got error %d", (int)errno);
}
return ret;
}
ssize_t
psync_file_pread(psync_file_t fd, void *buf, size_t count, uint64_t offset){
ssize_t ret;
ret=pread(fd, buf, count, offset);
if (unlikely(ret==-1)){
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while writing to file");
ret=pread(fd, buf, count, offset);
if (ret!=-1)
return ret;
}
debug(D_NOTICE, "got error %d", (int)errno);
}
return ret;
}
ssize_t
psync_file_write(psync_file_t fd, const void *buf, size_t count){
ssize_t ret;
ret=write(fd, buf, count);
if (unlikely(ret==-1)){
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while writing to file");
ret=write(fd, buf, count);
if (ret!=-1)
return ret;
}
debug(D_NOTICE, "got error %d", (int)errno);
}
return ret;
}
ssize_t
psync_file_pwrite(psync_file_t fd, const void *buf, size_t count, uint64_t offset){
ssize_t ret;
ret=pwrite(fd, buf, count, offset);
if (unlikely(ret==-1)){
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while writing to file");
ret=pwrite(fd, buf, count, offset);
if (ret!=-1)
return ret;
}
debug(D_NOTICE, "got error %d", (int)errno);
}
return ret;
}
int64_t
psync_file_seek(psync_file_t fd, uint64_t offset, int whence){
return lseek(fd, offset, whence);
}
int
psync_file_truncate(psync_file_t fd){
off_t off;
off=lseek(fd, 0, SEEK_CUR);
if (likely_log(off!=(off_t)-1)){
if (unlikely(ftruncate(fd, off))){
while (errno==EINTR){
debug(D_NOTICE, "got EINTR while truncating file");
if (!ftruncate(fd, off))
return 0;
}
debug(D_NOTICE, "got error %d", (int)errno);
return -1;
}
else
return 0;
}
else
return -1;
}
int64_t
psync_file_size(psync_file_t fd){
struct stat st;
if (unlikely_log(fstat(fd, &st)))
return -1;
else
return st.st_size;
}
void
psync_set_software_name(const char *snm){
psync_software_name=snm;
}
void
psync_set_os_name(const char *osnm){
psync_os_name=osnm;
}
char *
psync_deviceos(){
return psync_os_name?psync_strdup(psync_os_name):psync_deviceid();
}
char *
psync_device_string(){
#if defined(P_OS_LINUX)
char *osname=psync_deviceos();
char *ret = psync_strcat(osname, ", ", psync_software_name, NULL);
free(osname);
return ret;
#endif
return psync_strcat(psync_deviceid(), ", ", psync_software_name, NULL);
}
const char *
psync_appname(){
return psync_software_name;
}
char *
psync_deviceid(){
char *device;
#if defined(P_OS_LINUX)
DIR *dh;
//struct dirent entry; // DELETEME: not used
struct dirent *de;
const char *hardware;
char *path, buf[8];
int fd;
hardware="Desktop";
dh=opendir("/sys/class/power_supply");
if (dh){
//while (!readdir_r(dh, &entry, &de) && de) { // DELETEME: deprecated
while((de=readdir(dh))) {
if (de->d_name[0]!='.' || (de->d_name[1]!=0 && (de->d_name[1]!='.' || de->d_name[2]!=0))){
path=psync_strcat("/sys/class/power_supply/", de->d_name, "/type", NULL);
fd=open(path, O_RDONLY);
psync_free(path);
if (fd==-1)
continue;
if (read(fd, buf, 7)==7 && !memcmp(buf, "Battery", 7)){
close(fd);
hardware="Laptop";
break;
}
close(fd);
}
}
closedir(dh);
}
device=psync_strcat(hardware, ", Linux", NULL);
#else
device=psync_strcat("Desktop", NULL);
#endif
debug(D_NOTICE, "detected device: %s", device);
return device;
}
#define PSYNC_RUN_CMD "xdg-open" // XXX: verify this...
int
psync_run_update_file(const char *path){
#if defined(P_OS_LINUX)
pid_t pid;
debug(D_NOTICE, "running %s with "PSYNC_RUN_CMD, path);
pid=fork();
if (unlikely(pid==-1)){
debug(D_ERROR, "fork failed");
return -1;
}
else if (pid){
int status;
psync_milisleep(100);
if (waitpid(pid, &status, WNOHANG)==0)
return 0;
else
return -1;
}
else{
char *ex;
int fd;
fd=open("/dev/null", O_RDWR);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
setsid();
ex=psync_strcat(PSYNC_RUN_CMD" \"", path, "\"", NULL);
execl("/bin/sh", "/bin/sh", "-c", ex, NULL);
debug(D_ERROR, "exec of %s failed", ex);
psync_free(ex);
exit(1);
}
#else
return -1;
#endif
}
int
psync_invalidate_os_cache_needed(){
return 0;
}
extern int overlays_running;
void
psync_rebuild_icons(){
if (!overlays_running)
return;
return;
}
int
psync_invalidate_os_cache(const char *path){
return 0;
}
void *
psync_mmap_anon(size_t size){
#if defined(PSYNC_MAP_ANONYMOUS)
return mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|PSYNC_MAP_ANONYMOUS, -1, 0);
#else
return malloc(size);
#endif
}
PSYNC_NOINLINE static void *
psync_mmap_anon_emergency(size_t size){
void *ret;
debug(D_WARNING, "could not allocate %lu bytes", (unsigned long)size);
psync_try_free_memory();
ret=psync_mmap_anon(size);
if (likely(ret))
return ret;
else{
debug(D_CRITICAL, "could not allocate %lu bytes even after freeing some memory, aborting", (unsigned long)size);
abort();
return NULL;
}
}
void *
psync_mmap_anon_safe(size_t size){
void *ret;
ret=psync_mmap_anon(size);
if (likely(ret))
return ret;
else
return psync_mmap_anon_emergency(size);
}
int
psync_munmap_anon(void *ptr, size_t size){
#if defined(PSYNC_MAP_ANONYMOUS)
return munmap(ptr, size);
#else
free(ptr);
return 0;
#endif
}
void
psync_anon_reset(void *ptr, size_t size){
#if defined(PSYNC_MAP_ANONYMOUS) && defined(MADV_DONTNEED)
madvise(ptr, size, MADV_DONTNEED);
#endif
}
int
psync_mlock(void *ptr, size_t size){
#if defined(_POSIX_MEMLOCK_RANGE)
return mlock(ptr, size);
#else
return -1;
#endif
}
int
psync_munlock(void *ptr, size_t size){
#if defined(_POSIX_MEMLOCK_RANGE)
return munlock(ptr, size);
#else
return -1;
#endif
}
int
psync_get_page_size(){
return psync_page_size;
}