tuyau

Client/server for transfering files (like cp)
git clone https://noulin.net/git/tuyau.git
Log | Files | Refs

commit 664248a9da8c2e92a073b561c7310798a5f4fb5f
Author: Remy Noulin <loader2x@gmail.com>
Date:   Fri, 12 Aug 2022 06:17:08 -0400

simple client to a single file to server

implemented in sclient and sserver

no responses

.gitignore                    |  63 +++++++++
NOTES.md                      |  50 +++++++
netFrame.c                    | 310 ++++++++++++++++++++++++++++++++++++++++++
netFrame.h                    | 207 ++++++++++++++++++++++++++++
netFrameInternal.h            |   7 +
package.yml                   |  37 +++++
sclient.c                     | 205 ++++++++++++++++++++++++++++
shpPackages/short/main.c      |  29 ++++
shpPackages/short/package.yml |  15 ++
shpPackages/short/short.h     |  15 ++
sserver.c                     | 196 ++++++++++++++++++++++++++
tuyau.c                       |  26 ++++
12 files changed, 1160 insertions(+)

Diffstat:
A.gitignore | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANOTES.md | 50++++++++++++++++++++++++++++++++++++++++++++++++++
AnetFrame.c | 310+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AnetFrame.h | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AnetFrameInternal.h | 8++++++++
Apackage.yml | 38++++++++++++++++++++++++++++++++++++++
Asclient.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AshpPackages/short/main.c | 29+++++++++++++++++++++++++++++
AshpPackages/short/package.yml | 15+++++++++++++++
AshpPackages/short/short.h | 15+++++++++++++++
Asserver.c | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atuyau.c | 27+++++++++++++++++++++++++++
12 files changed, 1163 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,63 @@ +# Vim +*.sw* + +# Debug +.gdb_history + +# Coverage +*.gcov +*.gcda +*.gcno + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/NOTES.md b/NOTES.md @@ -0,0 +1,50 @@ +# to root path +x./sclient.c NOTES.md dev +x./sclient.c ~/notes.txt dev +# to path +x./sclient.c NOTES.md dev:tuyauTest/ +# new name +x./sclient.c NOTES.md dev:tuyauTest/ger.md + +./sclient.c dev:NOTES.md +./sclient.c dev:NOTES.md ../ + +cp file tuyau://ip/path + +>> ADD FILE MODE +frame: +1 byte command +8 bytes token +2 bytes file name length +<filename> +2 bytes path length +<path> +- optional +8 bytes data size +<data> + +> client/server requests +client > command + reponse < server 1 byte - 0 ok/1 error + <data> + +port 1032 + +- config home ~/.tuyau/config.yml +--- + alias: + hostname: ip + token: QE123123 + root: /home/rlp/tmp/ + port: 1022 +serverConfig.yml +--- + port: 1023 // optional + user: + token: QE123123 + root: /home/rlp/tmp/ +- send files with glob, recursive +- received files, glob, recursive +- list files in path +- token, home directory +- server name ip port in config diff --git a/netFrame.c b/netFrame.c @@ -0,0 +1,310 @@ + + +/* Libsheepy documentation: https://spartatek.se/libsheepy/ */ + +#include "libsheepyObject.h" +#include "netFrame.h" +#include "netFrameInternal.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> + +void initiateNetFrame(netFramet *self); +void registerMethodsNetFrame(netFrameFunctionst *f); +void initiateAllocateNetFrame(netFramet **self); +void finalizeNetFrame(void); +netFramet* allocNetFrame(void); +local void freeNetFrame(netFramet *self); +local void terminateNetFrame(netFramet **self); +local char* toStringNetFrame(netFramet *self); +local netFramet* duplicateNetFrame(netFramet *self); +local void smashNetFrame(netFramet **self); +local void finishNetFrame(netFramet **self); +local const char* helpNetFrame(netFramet *self); +local void setCallbackNetFrame(netFramet *self, callBackNetFramet callback, void* context); +local bool sendNetFrame(netFramet *self, int sock, void* buf, size_t size); +local bool receiveNetFrame(netFramet *self, int sock); +local bool receiveOneNetFrame(netFramet *self, int sock); +local bool endNetFrame(netFramet *self, int sock); + +/* enable/disable logging */ +/* #undef pLog */ +/* #define pLog(...) */ + +bool checkLibsheepyVersionNetFrame(const char *currentLibsheepyVersion) { + return eqG(currentLibsheepyVersion, LIBSHEEPY_VERSION); +} + +void initiateNetFrame(netFramet *self) { + + self->type = "netFrame"; + if (!netFrameF) { + netFrameF = malloc(sizeof(netFrameFunctionst)); + registerMethodsNetFrame(netFrameF); + pErrorNot0(atexit(finalizeNetFrame)); + } + self->f = netFrameF; + + self->callback = null; + self->context = null; +} + +void registerMethodsNetFrame(netFrameFunctionst *f) { + + f->free = freeNetFrame; + f->terminate = terminateNetFrame; + f->toString = toStringNetFrame; + f->duplicate = duplicateNetFrame; + f->smash = smashNetFrame; + f->finish = finishNetFrame; + f->help = helpNetFrame; + f->setCallback = setCallbackNetFrame; + f->send = sendNetFrame; + f->receive = receiveNetFrame; + f->receiveOne = receiveOneNetFrame; + f->end = endNetFrame; +} + +void initiateAllocateNetFrame(netFramet **self) { + + if (self) { + (*self) = malloc(sizeof(netFramet)); + if (*self) { + initiateNetFrame(*self); + } + } +} + +void finalizeNetFrame(void) { + + if (netFrameF) { + freen(netFrameF); + } +} + +netFramet* allocNetFrame(void) { + netFramet *r = NULL; + + initiateAllocateNetFrame(&r); + ret r; +} + + +void cleanUpNetFrameTerminateG(netFramet **val) { + terminateO(*val); +} + +void cleanUpNetFrameFreeG(netFramet *val) { + freeO(val); +} + +void cleanUpNetFrameFinishG(netFramet *val) { + finishO(val); +} + +local void freeNetFrame(netFramet *self UNUSED) { +} + +local void terminateNetFrame(netFramet **self) { + + freeNetFrame(*self); + finishNetFrame(self); +} + + +local char* toStringNetFrame(netFramet *self UNUSED) { + + ret strdup("TODO - netFrame"); +} + +local netFramet* duplicateNetFrame(netFramet *self UNUSED) { + + createAllocateNetFrame(dup); + ret dup; +} + +local void smashNetFrame(netFramet **self) { + + finishNetFrame(self); +} + +#if NFreeStackCheck +local void finishNetFrame(netFramet **self) { + + register u64 rsp asm("rsp"); + if ((u64)*self > rsp) { + logW("Probably trying to free a smallArray on stack: "BLD"%p"RST" sp: "BLD"%p"RST, *self, rsp); + logBtrace; + } + else { + freen(*self); + } +} +#else +// #if NFreeStackCheck +local void finishNetFrame(netFramet **self) { + + freen(*self); +} +#endif +// #if NFreeStackCheck + +local const char* helpNetFrame(netFramet UNUSED *self) { + ret "TODO - netFrame help"; +} + + +local void setCallbackNetFrame(netFramet *self, callBackNetFramet callback, void* context) { + self->callback = callback; + self->context = context; +} + + +local bool sendNetFrame(netFramet *self UNUSED, int sock, void* buf, size_t size) { + + ssize_t status; // for netsend + + #define netsend(B,SZ,FLGS) procbegin\ + UNIQVAR(retry):\ + if ((status = send(sock, B, SZ, (FLGS) | MSG_NOSIGNAL)) < 0) {\ + if (errno == EAGAIN or errno == EINTR) {\ + /* TODO this happens when the socket is in nonblocking mode */\ + /* in my tests, there are data losses when size is larger than 1MBytes */\ + /* this is a simple solution equivalent to blocking */\ + /* a better solution is to keep running in the event loop */\ + /* for large transfers */\ + /* When EINTR is received, the send call was interrupted by a signal */\ + /* restart the send call when it happens */\ + goto UNIQVAR(retry);\ + }\ + shPrintError;\ + shperror("Netframe send socket error");\ + ret no;\ + }\ + procend + + if (size > 0) { + netsend(&size, sizeof(size), MSG_MORE); + } + else { + // size 0 is the last frame in this transaction + netsend(&size, sizeof(size), 0); + ret yes; + } + + while(size > 0) { + netsend(buf, size, 0); + buf += status; + size -= status; + } + + ret yes; +} + + +local bool receiveNetFrame(netFramet *self, int sock) { + if (!self->callback) ret no; + + size_t size; + ssize_t status; // for netrecv + bool isFirstFrame = yes; + + #define netrecv(B, SZ, FLGS, CHECK, ENOUGH, ERROR) procbegin\ + UNIQVAR(retry):\ + if ((status = recv(sock, B, SZ, FLGS)) < 0) {\ + if (errno == EAGAIN or errno == EINTR) {\ + /* TODO this happens when the socket is in nonblocking mode */\ + /* this is a simple solution equivalent to blocking */\ + /* a better solution is to keep running in the event loop */\ + /* for large transfers */\ + /* When EINTR is received, the recv call was interrupted by a signal */\ + /* restart the recv call when it happens */\ + goto UNIQVAR(retry);\ + }\ + shPrintError;\ + shperror("Netframe recv socket error");\ + ERROR;\ + ret no;\ + }\ + elif (!status) {\ + /* ending connection - stop receiving frames */\ + CHECK;\ + }\ + procend + + forever { + netrecv(&size, sizeof(size), MSG_WAITALL, logE("ending connection");ret no,,logE("recv frame size")); + + if (size == 0) { + /* the last frame for this transaction is received, leave this loop */ + if (isFirstFrame) { + /* the first frame has size 0, this is a request to close the connection */ + logD("close socket"); + close(sock); + } + break; + } + + //logVarG(size); + + cleanCharP(buf) = malloc(size); + if (!buf) ret no; + + void *b = buf; + size_t sz = size; + while (sz > 0) { + netrecv(b, sz, MSG_WAITALL /*flags*/, logE("ending connection: received %i, expected %i, left to receive %i", size-sz, size, sz);ret no/*frame is incomplete*/,, logE("Received: %i, left to receive: %i", size-sz ,sz)); + b += status; + sz -= status; + } + + if (!self->callback(buf, size, self->context)) { + // TODO error + } + isFirstFrame = no; + } + + ret yes; +} + +local bool receiveOneNetFrame(netFramet *self, int sock) { + if (!self->callback) ret no; + + size_t size; + ssize_t status; // for netrecv + + netrecv(&size, sizeof(size), MSG_WAITALL, logI("ending connection");ret no/*frame is incomplete*/,,logE("recv size")); + + //logVarG(size); + + if (!size) { + // a frame size 0 is a request to close the connection + logD("close socket"); + close(sock); + ret yes; + } + + cleanCharP(buf) = malloc(size); + if (!buf) ret no; + + void *b = buf; + size_t sz = size; + while (sz > 0) { + netrecv(b, sz, MSG_WAITALL /*flags*/, logI("ending connection");ret no/*frame is incomplete*/, logD("Received: %i, left to receive: %i", size-sz ,sz), logE("Received: %i, left to receive: %i", size-sz ,sz)); + b += status; + sz -= status; + } + + if (!self->callback(buf, size, self->context)) { + // TODO error + } + ret yes; +} + +local bool endNetFrame(netFramet *self, int sock) { + ret sendNetFrame(self, sock, null, 0); +} + + +// vim: set expandtab ts=2 sw=2: diff --git a/netFrame.h b/netFrame.h @@ -0,0 +1,207 @@ +#pragma once + +/* Libsheepy documentation: https://spartatek.se/libsheepy/ */ + +/** + * netFrame is client and server for sending and receiving data frames + * the sockets can remain open all the time + * + * A data frame is a buffer and a size. + * + * Frame: + * -------------- ------ + * | size 8 bytes | data + * -------------- ------ + * + * The client opens a socket and calls send with the buffer address and size: + * + * cleanAllocateNetFrame(netframe); + * Loop until all frames are sent: + * o(netframe,send , sock, buffer, size); + * Send a frame with size 0 with no data to signal the end of the transfer + * when a frame size 0 is received in the server, the callback is not called + * The socket can be kept open + * + * The server sets a callback before receiving connections + * + * cleanAllocateNetFrame(netframe); + * o(netframe, setCallback, cb, NULL context); + * + * The callback is executed after each frame is completely received. + * The callback prototype is: + * + * callBackNetFramet: + * bool cb(void *buf, size_t size, void *context); + * + * The provided buffer buf is freed in netFrame. + * + * In the event loop, the server accepts connections and call receive: + * + * var r = o(netframe,receive, mysock); + * + * receive waits for frames until a frame size 0 is received + */ + +#include "libsheepyObject.h" + +/* Class netFrame */ +typ struct netFrame netFramet; + +#define isONetFrame(obj) isOType(obj, "netFrame") +#define isONetFrametG isONetFrame + +/* for object inheriting netFrame, cast to netFrame to be able to use this class functions and generics*/ +#define cNetFrame(self) ( (netFramet*) self ) + +typ void (*freeNetFrameFt) (netFramet *self); +typ void (*terminateNetFrameFt) (netFramet **self); +typ char* (*toStringNetFrameFt) (netFramet *self); +typ netFramet* (*duplicateNetFrameFt) (netFramet *self); +typ void (*smashNetFrameFt) (netFramet **self); + +/** + * free netFrame + */ +typ void (*finishNetFrameFt) (netFramet **self); + +typ const char* (*helpNetFrameFt) (netFramet *self); + +/** + * callback is a function called after each frame is received + * + * \param + * buf data from the network, netFrame frees the buffer + * \param + * size data szie from the network + * \param + * context callback context variable + */ +typ bool (*callBackNetFramet)(void *buf, size_t size, void *context); + +/** + * set callback called after each received frame in the receive function + * and context for the callback function + */ +typ void (*setCallbackNetFrameFt)(netFramet *self, callBackNetFramet callback, void* context); + +/** + * send buf, size 0 means end of transaction + */ +typ bool (*sendNetFrameFt)(netFramet *self, int sock, void* buf, size_t size); + +/** + * receive frames until the socket is closed by the client + */ +typ bool (*receiveNetFrameFt)(netFramet *self, int sock); + +/** + * receive one frame and return + */ +typ bool (*receiveOneNetFrameFt)(netFramet *self, int sock); + +/** + * end transaction or inform the server that the connection is going to be closed + * this function sends a frame of size 0 + * when one frame is sent: + * - the function send sends the size and the buffer + * - sending a frame size 0 closes the connection + * when multiple frames are sent: + * - call send as many times as needed to send buffer of size not 0 + * - when finished send a frame size 0 meaning end of transaction + * - to close the connection, send a frame size 0 again + */ +typ bool (*endNetFrameFt)(netFramet *self, int sock); + +/** + * class functions + * allocated once for all objects + * + * freed with finalizeNetFrame + */ + +/** + * use this define in child classes and add the new function after this class functions + * + * in this define, add the methods after <finishNetFrameFt finish;> + * + * Example: + * #define RINGFUNCTIONST \n * NETFRAMEFUNCTIONST; \n * setSizeRingFt setSize + */ +#define NETFRAMEFUNCTIONST \ + helpNetFrameFt help;\ + setCallbackNetFrameFt setCallback;\ + sendNetFrameFt send;\ + receiveNetFrameFt receive;\ + receiveOneNetFrameFt receiveOne;\ + endNetFrameFt end + +typ struct { + freeNetFrameFt free; + terminateNetFrameFt terminate; + toStringNetFrameFt toString; + duplicateNetFrameFt duplicate; + smashNetFrameFt smash; + finishNetFrameFt finish; + NETFRAMEFUNCTIONST; +} netFrameFunctionst; + +/** + * class + */ +struct netFrame { + const char *type; + netFrameFunctionst *f; + + callBackNetFramet callback; + void *context; +}; + +/* netFrame */ + +/** return true when this object file was compiled with current libsheepy version */ +#define isNetFrameCompiledWithCurrentLisheepyVersion checkLibsheepyVersionNetFrame(LIBSHEEPY_VERSION) +bool checkLibsheepyVersionNetFrame(const char *currentLibsheepyVersion); + +#define createNetFrame(obj) ;netFramet obj; initiateNetFrame(&obj) +#define createAllocateNetFrame(obj) ;netFramet *obj; initiateAllocateNetFrame(&obj) + +void initiateNetFrame(netFramet *self); +void initiateAllocateNetFrame(netFramet **self); +void finalizeNetFrame(void); + +/* initialize class methods, call registerMethodsNetFrame from classes inheriting this class */ +void registerMethodsNetFrame(netFrameFunctionst *f); + +netFramet* allocNetFrame(void); + +/* terminate netFramet val when it is out of scope */ +void cleanUpNetFrameTerminateG(netFramet **val); + +/* free netFramet val when it is out of scope */ +void cleanUpNetFrameFreeG(netFramet *val); + +/* finish netFramet val when it is out of scope */ +void cleanUpNetFrameFinishG(netFramet *val); + +/** + * declare pointer name with Type netFramet and terminate name when it is out of scope + */ +#define cleanNetFrameP(name) netFramet *name CLEANUP(cleanUpNetFrameTerminateG) + +/** + * allocate NetFrame (pointer) and clean up when it is out of scope + */ +#define cleanAllocateNetFrame(obj) ;cleanNetFrameP(obj); initiateAllocateNetFrame(&obj) + +/** + * declare local object name with Type netFramet and free name when it is out of scope + */ +#define cleanNetFrame(name) netFramet name CLEANUP(cleanUpNetFrameFreeG); initiateNetFrame(&name) + +/** + * declare pointer name with Type netFramet and finish name when it is out of scope + */ +#define cleanFinishNetFrameP(name) netFramet *name CLEANUP(cleanUpNetFrameFinishG) + +/* end class netFrame*/ +// vim: set expandtab ts=2 sw=2: diff --git a/netFrameInternal.h b/netFrameInternal.h @@ -0,0 +1,7 @@ +#pragma once + +static netFrameFunctionst *netFrameF = NULL; + +/* TODO declare structs for private data and add a void pointer to the private data in the class declaration */ + +// vim: set expandtab ts=2 sw=2:+ \ No newline at end of file diff --git a/package.yml b/package.yml @@ -0,0 +1,37 @@ +--- + name: tuyau + version: 0.0.1 + description: "explanation" + bin: ./tuyau.c + #cflags: -DA -g3 -std=gnu11 -fPIC -pipe + #lflags: -lpcre + repository: + type: git + url: git+https://github.com/USER/tuyau.git + keywords: + #- utility + - command + author: Anonymous + license: MIT + bugs: + url: https://github.com/USER/tuyau/issues + homepage: https://github.com/USER/tuyau#readme + #compileHelp: # text displayed when there is a compilation error + #dependencies: + # md4c: + # Test configuration: + #testBin: ./testTuyau.c + #testCflags: -g3 -std=gnu11 -fPIC -pipe -fprofile-arcs -ftest-coverage -Wall -Wextra + #testLflags: -lcheck_pic -lrt -lm -lsubunit -fprofile-arcs -ftest-coverage -rdynamic + # Memcheck configuration (sheepy -m): + #memcheckBin: ./memcheckTuyau.c + #memcheckCmd: valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 + #memcheckCflags: -g3 -std=gnu11 -fPIC -pipe + #memcheckLflags: -rdynamic + # The asan* are the options for building the libsasan tests (sheepy -a): + #asanBin: ./asanTuyau.c + #asanCmd: env ASAN_OPTIONS="detect_leaks=1:detect_stack_use_after_return=1:halt_on_error=0:log_path=stdout:color=always:print_cmdline=1" + #asanCflags: -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address # these flags are overriden by package.yml + #asanLflags: -rdynamic -fsanitize=address -lasan # these flags are overriden by package.yml + #documentationCmd: # command for generating the documentation with spm doc + private: false # true for private package+ \ No newline at end of file diff --git a/sclient.c b/sclient.c @@ -0,0 +1,205 @@ +#! /usr/bin/env sheepy +/* or direct path to sheepy: #! /usr/local/bin/sheepy */ + +/* Libsheepy documentation: https://spartatek.se/libsheepy/ */ +#include "libsheepyObject.h" +#include <netdb.h> +#include <netinet/in.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +// open +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "netFrame.h" +#include "shpPackages/short/short.h" + +#define CONFIG "~/.tuyau/config.yml" + +int main(int ARGC, char** ARGV) { + + initLibsheepy(ARGV[0]); + setLogMode(LOG_FUNC); + + cleanCharP(config) = expandHome(CONFIG); + cleanAllocateSmallJson(cfg); + readFileG(cfg, config); + + if (ARGC < 2) { + logE("Missing arguments"); + ret 1; + } + + // check args + if (ARGC == 2) { + TODO("check that a server is selected"); + } + + if (ARGC < 3) { + logE("Missing arguments"); + ret 1; + } + + bool isReceive = no; + iter(cfg, D) { + cast(smallDictt*,d,D); + //lv(d); + if (startsWithG(ARGV[1], iK(cfg)) and ARGV[1][lenG(iK(cfg))] == ':' and ARGV[1][lenG(iK(cfg))+1] != 0) { + TODO("arg 2 must be a path in client"); + isReceive = yes; + break; + } + } + + char *sendFile = null; + const char *serverName = null; + char *destPath = null; + if (not isReceive) { + logD("path in client"); + if (not isPath(ARGV[1])) { + logE("Incorrect path: "BLD YLW"%s"RST,ARGV[1]); + ret 1; + } + sendFile = ARGV[1]; + + // check last arg + bool foundDest = no; + iter(cfg, D) { + cast(smallDictt*,d,D); + //lv(d); + if (startsWithG(ARGV[ARGC-1], iK(cfg))) { + if (ARGV[ARGC-1][lenG(iK(cfg))] == 0) { + logD("no dest path"); + serverName = iK(cfg); + foundDest = yes; + break; + } + elif (ARGV[ARGC-1][lenG(iK(cfg))] == ':' and ARGV[ARGC-1][lenG(iK(cfg))+1] != 0) { + logD("found destination"); + serverName = iK(cfg); + destPath = ARGV[ARGC-1] + lenG(iK(cfg)) + 1; + foundDest = yes; + break; + } + } + } + } // not isReceive + + if (not serverName) { + logE("server name not found"); + ret 1; + } + + cleanFinishSmallDictP(svrInfo) = getG(cfg, rtSmallDictt, serverName); + + lv(svrInfo); + + int sock; + struct sockaddr_in server; + struct hostent *hp; + int mysock; + int rval; + + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + perror("Failed to create socket"); + } + + server.sin_family = AF_INET; + + hp = gethostbyname($(svrInfo, "hostname")); + if (hp==0) { + perror("gethostbyname failed"); + close(sock); + XFailure; + } + + memcpy(&server.sin_addr, hp->h_addr, hp->h_length); + server.sin_port = htons(u$(svrInfo, "port")); + + if (connect(sock,(struct sockaddr *) &server, sizeof(server))){ + perror("connect failed"); + close(sock); + XFailure; + } + + // send + // command 0 + // token + // file name max 8k + // dest path 0 + + u8 buf[1024*1024] = init0Var; + + // command + buf[0] = 0; + // token + memcpy(buf + 1, $(svrInfo, "token"), 8); + u16 *filenameLength = (u16*)(buf + 9); + char *filename = basename(sendFile); + *filenameLength = lenG(filename); + memcpy(buf + 11, filename, lenG(filename)); + u32 bufi = 11 + lenG(filename); + u16 *filemode = (u16*)(buf + bufi); + struct stat st; + if(stat(sendFile, &st) != 0){ + logE("Can't access %s", sendFile); + ret 1; + } + *filemode = st.st_mode & 0xFFF; + bufi += 2; + u16 *pathLength = (u16*)(buf + bufi); + *pathLength = lenG(destPath); + bufi += 2; + if (destPath) { + memcpy(buf + bufi, destPath, *pathLength); + bufi += *pathLength; + } + // file size + u64 *filesize = (u64*)(buf + bufi); + *filesize = fileSizeG(sendFile); + bufi += 8; + + cleanAllocateNetFrame(netframe); + + if (*filesize < (sizeof(buf) - bufi)) { + pError0(bReadFile(sendFile, buf + bufi)); + o(netframe,send , sock, buf, bufi + *filesize); + } + else { + logD("big file"); + u64 szToSend = *filesize; + u8 *b = buf + bufi; + u64 bSz = sizeof(buf) - bufi; + int fd = open(sendFile, O_RDONLY); + do { + if (szToSend <= bSz) { + read(fd, b, szToSend); + o(netframe,send , sock, b, szToSend); + szToSend = 0; + } + else { + read(fd, b, bSz); + if (szToSend == *filesize) { + o(netframe,send , sock, buf, bufi + bSz); + } + else { + o(netframe,send , sock, b, bSz); + } + szToSend -= bSz; + } + } while(szToSend); + close(fd); + } + + + // last frame size 0 + o(netframe,end, sock); + + close(sock); +} +// vim: set expandtab ts=2 sw=2: diff --git a/shpPackages/short/main.c b/shpPackages/short/main.c @@ -0,0 +1,29 @@ +#! /usr/bin/env sheepy +/* or direct path to sheepy: #! /usr/local/bin/sheepy */ + +/* Libsheepy documentation: http://spartatek.se/libsheepy/ */ +#include "libsheepyObject.h" +#include "short.h" + +int argc; char **argv; + +/* enable/disable logging */ +/* #undef pLog */ +/* #define pLog(...) */ + +int main(int ARGC, char** ARGV) { + + argc = ARGC; argv = ARGV; + + initLibsheepy(ARGV[0]); + + createSmallDict(d); + + $(&d, "wer"); + + i_("de"); + + lv(&d); + +} +// vim: set expandtab ts=2 sw=2: diff --git a/shpPackages/short/package.yml b/shpPackages/short/package.yml @@ -0,0 +1,15 @@ +--- + name: short + version: 0.0.3 + description: "convenient defines for often used libsheepy function calls making the lines shorter and more readable" + bin: ./short.h + repository: + type: git + url: git+https://github.com/USER/short.git + keywords: + - utility + author: Remy + license: MIT + bugs: + url: https://github.com/USER/short/issues + homepage: https://github.com/USER/short#readme diff --git a/shpPackages/short/short.h b/shpPackages/short/short.h @@ -0,0 +1,15 @@ +#include "libsheepyObject.h" + +#define lv logTVarG +#define i_ logI + +#define iI iterIndexG +#define iK iterKeyG + +// get string from array or dict +#define $(object, key) getG(object, rtChar, key) + +// get unsigned int +#define u$(object, key) getG(object, rtU64, key) + +// vim: set expandtab ts=2 sw=2: diff --git a/sserver.c b/sserver.c @@ -0,0 +1,196 @@ +#! /usr/bin/env sheepy +/* or direct path to sheepy: #! /usr/local/bin/sheepy */ + +/* Libsheepy documentation: https://spartatek.se/libsheepy/ */ +#include "libsheepyObject.h" +#include <netinet/in.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +// open +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "netFrame.h" +#include "shpPackages/short/short.h" + +#define CONFIG "~/.tuyau/serverConfig.yml" + +int packetCount = 0; +int fd = -1; +u64 transferSz = 0; + +smallJsont *tokens = null; + +void saveReceivedData(void *receiveB, size_t sz); + +bool cb(void *buf, size_t size, void *context) { + saveReceivedData(buf, size); + ret yes; +} + +int main(int ARGC, char** ARGV) { + + initLibsheepy(ARGV[0]); + setLogMode(LOG_FUNC); + + cleanCharP(config) = expandHome(CONFIG); + cleanAllocateSmallJson(cfg); + readFileG(cfg, config); + + tokens = allocG(rtSmallJsont); + + iter(cfg, D) { + cast(smallDictt*,d,D); + setG(tokens, $(d,"token"), $(d,"root")); + } + + lv(tokens); + + int sock; + struct sockaddr_in server; + int mysock; + int rval; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + perror("Failed to create socket"); + XFailure; + } + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(1032); + + if (bind(sock, (struct sockaddr *) &server, sizeof(server))){ + perror("bind failed"); + XFailure; + } + + cleanAllocateNetFrame(netframe); + + o(netframe, setCallback, cb, NULL /*context*/); + + listen(sock, 5); + forever { + mysock = accept(sock, (struct sockaddr *)0, 0); + if (mysock == 1) + perror("accept failed"); + else { + var r = o(netframe,receive, mysock); + logVarG(r); + } + } + + terminateG(tokens); +} + +void saveReceivedData(void *receiveB, size_t sz) { + logVarG(sz); + if (packetCount == 0) { + // header + u8 *command = (u8*) receiveB; + u8 *token = (u8*) (receiveB + 1); + char tk[9] = init0Var; + memcpy(tk, token, 8); + + if (not hasG(tokens, tk)) { + logE("token not found"); + ret; + } + + char *root = $(tokens, tk); + + u16 *filenameLength = (u16*)(receiveB + 9); + // receiveB + 11 = filename + cleanCharP(filename) = malloc(*filenameLength + 1); + filename[*filenameLength] = 0; + memcpy(filename, receiveB + 11, *filenameLength); + u32 bufi = 11 + *filenameLength; + u16 *filemode = (u16*)(receiveB + bufi); + bufi += 2; + u16 *pathLength = (u16*)(receiveB + bufi); + bufi += 2; + cleanCharP(destPath) = null; + if (*pathLength) { + destPath = malloc(*pathLength + 1); + memcpy(destPath, receiveB + bufi, *pathLength); + destPath[*pathLength] = 0; + bufi += *pathLength; + } + // file size + u64 *filesize = (u64*)(receiveB + bufi); + bufi += 8; + + logD("bufi %d fsz %d + %d, filename %s, destPath %s",bufi, *filesize, bufi + *filesize, filename, destPath); + + char *path = catS(root, "/", nS(destPath)); + pErrorNULL(normalizePathG(&path)); + pErrorNULL(trimG(&path)); + cleanCharP(nroot) = normalizePathG(root); + //lv(path); + if (not startsWithG(path, nroot)) { + logE("Incorrect path"); + ret; + } + + // check isdir or if there is a filename + bool filenameInDestPath = no; + if (destPath and not isPath(path)) { + if (endsWithG(destPath,'/')) { + logE("Directory doesn't exist %s", path); + ret; + } + cleanCharP(dir) = shDirname(path); + if (not isPath(dir)) { + logE("Directory doesn't exist %s", dir); + ret; + } + filenameInDestPath = yes; + } + //lv(filenameInDestPath); + if (not filenameInDestPath) { + pErrorNULL(iAppendManyS(&path, "/", filename)); + } + lv(path); + + if (*filesize <= sz - bufi) { + pError0(writeFile(path, receiveB + bufi, *filesize)); + pError0(fileChmod(path, *filemode)); + } + else { + logD("open fd"); + inc packetCount; + fd = open(path, O_WRONLY | O_CREAT, *filemode); + if (fd == -1) { + logE("cant open %s", path); + ret; + } + int r = write(fd, receiveB + bufi, sz - bufi); + if (r == -1) { + logE("cant write %s", path); + ret; + } + transferSz = *filesize - (sz - bufi); + } + } + else { + if (fd == -1) ret; + // big file + int r = write(fd, receiveB, sz); + if (r == -1) { + logE("cant write"); + ret; + } + if (transferSz <= sz) { + transferSz = 0; + close(fd); + fd = -1; + packetCount = 0; + } + else transferSz -= sz; + } +} +// vim: set expandtab ts=2 sw=2: diff --git a/tuyau.c b/tuyau.c @@ -0,0 +1,26 @@ +#! /usr/bin/env sheepy +/* or direct path to sheepy: #! /usr/local/bin/sheepy */ + +/* Libsheepy documentation: https://spartatek.se/libsheepy/ */ +#include "libsheepyObject.h" + +int argc; char **argv; + +/* enable/disable logging */ +/* #undef pLog */ +/* #define pLog(...) */ + +int main(int ARGC, char** ARGV) { + + argc = ARGC; argv = ARGV; + + initLibsheepy(ARGV[0]); + setLogMode(LOG_VERBOSE); + //openProgLogFile(); + //setLogSymbols(LOG_UTF8); + //disableLibsheepyErrorLogs; + + logI("C template"); + +} +// vim: set expandtab ts=2 sw=2:+ \ No newline at end of file