Laboratoare

Programarea folosind BSD Sockets

Sockets

Designing a protocol

Part 1. BSD Sockets

The first assignment deals with the implementation of a small TCP application using BSD sockets. The application must consist as a server and at least one client that use the services provided by the server. Teams will propose specifications for the application by sending them by e-mail to the lab supervisor. Examples below.

The following tasks must be accomplished:

    • An application-level communication protocol

    • A server and at least a client that closesly follow the protocol. The programs must be written in C, using BSD sockets on Linux or UNIX.

    • [Optional] The project can be hosted on a public code repository (git or svn), or on the CS Department's Git server

The deadline for submitting the project is Week 3 of the semester (CTI-EN), and Week 5 (CTI-RO).

Examples of projects [RO]

Part 2. Distributed application

This part focuses on building from scratch a distributed application on Java EE or another platform of choice. The platform and the actual specification of the assignment must be negotiated/discussed with the lab coordinator.

The application consists of several distributed, interconnected software components, that work together for a common goal. There are a minimum of two types of components that must be implemented:

    • Servers, that maintain the global status of the application and provides its main functionality. Servers may serve a different geographical area, and/or follow a specific purpose within the application. Servers communicate with each other in order to update the status, maintain consistency, syncronize data, and so on.

    • Clients. They provide the users with a lightweight and possibly mobile interface, and communicate with the servers. There can be several types of clients: standard standalone applications featuring a platform-specific UI, Web applications, mobile, etc.

Clients and servers may communicate through technologies and techniques of choice, such as: HTTP, REST, RMI, etc.

Requirements that must be met:

CTI-RO: minimum 2 different types of servers AND 2 different types of clients

CTI-EN: minimum 2 different types of servers OR 2 different types of clients

#include <stdlib.h>

#include <stdio.h>

#include <sys/types.h>

#include <string.h>

#include <errno.h>

#include <dirent.h>

#include <unistd.h>

#include <sys/stat.h>

#include <limits.h>

int paths_count=0;

char paths[255][PATH_MAX];

void myErorr(char *msg){


fprintf(stderr,"%s:%s\n",msg,strerror(errno));

exit(errno);

}

int isDirectoryEmpty(char *dirPath)

{

char cmd[1024];

int status, exitcode;

snprintf(cmd, 1024, "test $(ls -A \"%s\" 2>/dev/null | wc -l) -ne 0", dirPath);

status = system(cmd);

exitcode = WEXITSTATUS(status);

return exitcode;

}

void getAllFilesPaths(char *dirPath)

{

DIR *dir;

struct dirent *in;

char *name;

struct stat info;

char cale[PATH_MAX], cale_link[PATH_MAX + 1];

int n;

char errorMessage[PATH_MAX+100];

if(!(dir = opendir(dirPath)))

{

snprintf(errorMessage, sizeof(errorMessage), "Error while openning directory:%s\n",dirPath);

myErorr(errorMessage);

}

errno=0;

while((in = readdir(dir))>0)

{

name = in->d_name;

if(strcmp(name, ".") == 0 || strcmp(name, "..")==0)

continue;

snprintf(cale, sizeof(cale), "%s/%s", dirPath, name);


if(lstat(cale, &info)<0)

{

snprintf(errorMessage, sizeof(errorMessage), "Error at lstat:%s\n",cale);

myErorr(errorMessage);

}

if(S_ISDIR(info.st_mode)){

getAllFilesPaths(cale);

if(isDirectoryEmpty(cale)){

printf("%s\n", cale);

}

}

else

if(S_ISLNK(info.st_mode))

{

n = readlink(cale, cale_link, sizeof(cale_link));

cale_link[n]='\0';

printf("%s\n",cale_link);

}

else

{

printf("%s\n", cale);

}

}

if(errno){

snprintf(errorMessage, sizeof(errorMessage), "Error reading directory:%s\n",dirPath);

myErorr(errorMessage);

}

if(closedir(dir)){

snprintf(errorMessage, sizeof(errorMessage), "Error closing directory:%s\n",dirPath);

myErorr(errorMessage);

}

}

int main(int argc, char *argv[])

{

if(argc != 2)

{

printf("Mod de utilizare: %s director\n", argv[0]);

exit(1);

}

getAllFilesPaths(argv[1]);

return 0;

}

#include <stdlib.h>

#include <stdio.h>

#include <sys/types.h>

#include <string.h>

#include <errno.h>

#include <dirent.h>

#include <unistd.h>

#include <sys/stat.h>

#include <limits.h>

int paths_count=0;

char paths[255][PATH_MAX];

void myErorr(char *msg){


fprintf(stderr,"%s:%s\n",msg,strerror(errno));

exit(errno);

}

int isEmptyDirectory(char *dirPath)

{

char cmd[1024];

int status, exitcode;

snprintf(cmd, 1024, "test $(ls -A \"%s\" 2>/dev/null | wc -l) -ne 0", dirPath);

status = system(cmd);

exitcode = WEXITSTATUS(status);

return exitcode;

}

void getFilesPaths(char *dirPath)

{

DIR *dir;

struct dirent *in;

char *name;

struct stat info;

char cale[PATH_MAX], cale_link[PATH_MAX + 1];

int n;

char errorMessage[PATH_MAX+100];

if(!(dir = opendir(dirPath)))

{

snprintf(errorMessage, sizeof(errorMessage), "Error while openning directory:%s\n",dirPath);

myErorr(errorMessage);

}

errno=0;

while((in = readdir(dir))>0)

{

name = in->d_name;

if(strcmp(name, ".") == 0 || strcmp(name, "..")==0)

continue;

snprintf(cale, sizeof(cale), "%s/%s", dirPath, name);


if(lstat(cale, &info)<0)

{

snprintf(errorMessage, sizeof(errorMessage), "Error at lstat:%s\n",cale);

myErorr(errorMessage);

}

if(S_ISDIR(info.st_mode)){

getFilesPaths(cale);

if(isEmptyDirectory(cale)){

strcpy(paths[paths_count++], cale);

}

}

else

if(S_ISLNK(info.st_mode))

{

n = readlink(cale, cale_link, sizeof(cale_link));

cale_link[n]='\0';

strcpy(paths[paths_count++], cale_link);

}

else

{

strcpy(paths[paths_count++], cale);

}

}

if(errno){

snprintf(errorMessage, sizeof(errorMessage), "Error reading directory:%s\n",dirPath);

myErorr(errorMessage);

}

if(closedir(dir)){

snprintf(errorMessage, sizeof(errorMessage), "Error closing directory:%s\n",dirPath);

myErorr(errorMessage);

}


}

void getAllFilesPaths(char *dirPath)

{

getFilesPaths(dirPath);

paths_count--;

strcpy(paths[paths_count],"");

}

int main(int argc, char *argv[])

{

if(argc != 2)

{

printf("Usage: %s director\n", argv[0]);

exit(1);

}

getAllFilesPaths(argv[1]);

for(int i=0;i<paths_count;i++){

printf("%s\n",paths[i]);

}

return 0;

}

client.c

#include <unistd.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "client.h"

char *ip;

int port;

int socketFileDescriptor;

struct sockaddr_in serverAddress;

char *root;

char errorMessage[PATH_MAX + 100];

char *formatdate(time_t val) {

char *str;

if ((str = (char *) malloc(48)) == NULL)

displayError("malloc() error.");

strftime(str, 48, "%Y%m%d%H%M.%S", localtime(&val));

return str;

}

int adjustTimestamp(char *filepath, char *timestamp) {

char cmd[PATH_MAX + 32];

int status, exitcode;

snprintf(cmd, sizeof(cmd), "touch -a -m -t %s \"%s\"", timestamp, filepath);

status = system(cmd);

exitcode = WEXITSTATUS(status);

return exitcode;

}

int getIndexFromServerFiles(FileMetadata *serverFiles, int count, char *path) {

int i;

for (i = 0; i < count; i++) {

if (!strcmp(serverFiles[i].path, path))

return i;

}

return -1;

}

int removeDirectory(char *directoryPath) {

char cmd[PATH_MAX + 64];

int status, exitcode;

snprintf(cmd, sizeof(cmd), "rm -rf \"%s\"", directoryPath);

status = system(cmd);

exitcode = WEXITSTATUS(status);

return exitcode;

}

int removeFile(char *path) {

char cmd[PATH_MAX + 64];

int status, exitcode;

snprintf(cmd, sizeof(cmd), "rm \"%s\"", path);

status = system(cmd);

exitcode = WEXITSTATUS(status);

return exitcode;

}

void getFilesToDelete(FileMetadata *serverFiles, int serverFilesCount) {

filesToDelete = (FileMetadata *) malloc(sizeof(FileMetadata));

for (int i = 0; i < pathsCount; i++) {

int toDelete = 1;

for (int j = 0; j < serverFilesCount; j++) {

if (!strcmp(ownFiles[i].path, serverFiles[j].path)) {

toDelete = 0;

break;

}

}

if (toDelete) {

filesToDelete = (FileMetadata *) realloc(filesToDelete, sizeof(FileMetadata) * (++fileToDeleteCount));

filesToDelete[fileToDeleteCount - 1] = ownFiles[i];

}

}

}

void getFilesToUpdate(FileMetadata *serverFiles, int serverFilesCount) {

filesToUpdate = (FileMetadata *) malloc(sizeof(FileMetadata));

for (int i = 0; i < serverFilesCount; i++) {

int different = 0;

int found = 0;

for (int j = 0; j < pathsCount; j++) {

if (!strcmp(serverFiles[i].path, filesToUpdate[j].path)) {

found = 1;

if (serverFiles[i].size != filesToUpdate[j].size ||

serverFiles[i].timeStamp != filesToUpdate[j].timeStamp ||

serverFiles[i].isRegularFile != filesToUpdate[j].isRegularFile) {

different = 1;

break;

}

}

}

if (different || !found) {

filesToUpdate = (FileMetadata *) realloc(filesToUpdate, sizeof(FileMetadata) * (++filesToUpdateCount));

filesToUpdate[filesToUpdateCount - 1] = serverFiles[i];

}

}

}

void handle() {

char buff[1024];

char newPath[PATH_MAX];

FileMetadata *serverFiles;

int serverFilesCount;

int n = 0, i, size;

/*

* Receiving files count from the server.

*/

n = read(socketFileDescriptor, buff, 10);

if (n < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError reading from socketFileDescriptor in clint.c :\n");

displayError(errorMessage);

}

buff[n] = 0;

serverFilesCount = atoi(buff);

#if defined DEBUG_MODE

printf("[debug - client]: server tells that are %d files there\n", serverFilesCount);

#endif

/*

* Allocate memory for the server files to check if there are outdated/files that need to be deleted.

*/

if ((serverFiles = (FileMetadata *) malloc(serverFilesCount * sizeof(FileMetadata))) == NULL) {

snprintf(errorMessage, sizeof(errorMessage), "\nError allocating memory for server file :\n");

displayError(errorMessage);

}

/*

* Reading them from the socket.

*/

for (i = 0; i < serverFilesCount; i++) {

if (read(socketFileDescriptor, &serverFiles[i], sizeof(FileMetadata)) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError reading server files in client.c :\n");

displayError(errorMessage);

}

}

/*

* Getting files that need to be updated/deleted.

*/

getFilesToUpdate(serverFiles, serverFilesCount);

getFilesToDelete(serverFiles, serverFilesCount);

/*

* Telling the server how many files need updated by the client.

*/

#if defined DEBUG_MODE

printf("[debug - client]: %d files need updated/added\n", filesToUpdateCount);

#endif

snprintf(buff, 10, "%d", filesToUpdateCount);

if (write(socketFileDescriptor, buff, 10) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError writing file to update count :\n");

displayError(errorMessage);

}

for (i = 0; i < filesToUpdateCount; i++) {

#if defined DEBUG_MODE

printf("[debug - client]: sending '%s' to server\n", filesToUpdate[i].path);

#endif

if (write(socketFileDescriptor, filesToUpdate[i].path, PATH_MAX)) {

snprintf(errorMessage, sizeof(errorMessage), "\nError writing file to update :\n");

displayError(errorMessage);

}

}

/*

* Updating the files from the server.

*/

for (i = 0; i < filesToUpdateCount; i++) {

int idx;

int serverFileIdx = getIndexFromServerFiles(serverFiles, serverFilesCount, filesToUpdate[i].path);

/*

* Building the absolute path.

*/

snprintf(newPath, PATH_MAX, "%s/%s", root, filesToUpdate[i].path);

#if defined DEBUG_MODE

printf("[debug - client]: file '%s' is being updated.\n", newPath);

#endif

if (!filesToUpdate[i].isRegularFile) // is a directory

{

mkdir(newPath, ACCESSPERMS);

}

else // is a file, get data from socket and write into it

{

int fd = open(newPath, O_CREAT | O_TRUNC | O_WRONLY, 0744);

off_t size;

if (fd < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError opening file: %s \n", newPath);

displayError(errorMessage);

}

if(read(socketFileDescriptor, buff, 12)<0){

snprintf(errorMessage, sizeof(errorMessage), "\nError reading from file in client.c: %s :\n", newPath);

displayError(errorMessage);

}

size = strtol(buff, NULL, 10);

if(read(socketFileDescriptor, buff, size)<0){

snprintf(errorMessage, sizeof(errorMessage), "\nError reading from file in client.c: %s :\n", newPath);

displayError(errorMessage);

}

if(write(fd, buff, size)<0){

snprintf(errorMessage, sizeof(errorMessage), "\nError writing from file in client.c: %s :\n", newPath);

displayError(errorMessage);

}

if (close(fd) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError closing file in client.c: %s :\n", newPath);

displayError(errorMessage);

}

}

char *date = formatdate(serverFiles[serverFileIdx].timeStamp);

adjustTimestamp(newPath, date);

#if defined DEBUG_MODE

printf("[debug - client]: timestamp=%s for file %s\n", date, filesToUpdate[i].path);

#endif

if ((idx = getPathIndex(newPath)) == -1) // file/dir does not exists in the list => add it

{

strcpy(ownFiles[pathsCount].path, newPath);

ownFiles[pathsCount].size = serverFiles[serverFileIdx].size;

ownFiles[pathsCount].timeStamp = serverFiles[serverFileIdx].timeStamp;

pathsCount ++;

}

else // update its stats

{

ownFiles[idx].size = serverFiles[serverFileIdx].size;

ownFiles[idx].timeStamp = serverFiles[serverFileIdx].timeStamp;

}

#if defined DEBUG_MODE

printf("[debug - client]: file '%s' successfully updated.\n", newPath);

#endif

if (date) {

free(date);

}

}

/*

* Deleting the files that are on the clients' machine but not on the server.

*/

for (i = 0; i < fileToDeleteCount; i++)

{

int idx, j;

snprintf(newPath, PATH_MAX, "%s/%s", root, filesToDelete[i].path);

#if defined DEBUG_MODE

printf("[debug - client]: file '%s' is being deleted.\n", newPath);

#endif

if (!filesToDelete[i].isRegularFile) // is a directory

{

removeDirectory(newPath);

}

else // is a file, remove it

{

if (strlen(newPath) == strlen(root) + strlen(filesToDelete[i].path) + 1)

removeFile(newPath);

}

if ((idx = getPathIndex(newPath)) != -1)

{

for (j = idx; j < pathsCount - 1; j++)

ownFiles[j] = ownFiles[j + 1];

ownFiles --;

}

#if defined DEBUG_MODE

printf("[debug - client]: file '%s' successfully deleted.\n", newPath);

#endif

}

if (serverFiles) {

free(serverFiles);

}

}

void clientSetup() {

if ((socketFileDescriptor = socket(PF_INET, SOCK_STREAM, 0)) < 0) {

exit(EXIT_FAILURE);

}

serverAddress.sin_family = AF_INET;

serverAddress.sin_addr.s_addr = inet_addr(ip);

serverAddress.sin_port = htons(port);

if (connect(socketFileDescriptor, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {

exit(EXIT_FAILURE);

}

getAllFilesPaths(root);

getAllFilesMetadata(root);

handle();

}

int main(int argc, char *argv[]) {

if (argc != 3) {

printf("USAGE: %s <config_file_path> <root>.\n", argv[0]);

exit(1);

}

if (argv[2][strlen(argv[2]) - 1] == '/') {

printf("[ERROR]: Root shouldn't end with the '/' character.\n");

exit(1);

}

readConfigurationParameters(argv[1]);

root = argv[2];

clientSetup();

return 0;

}

client.h

#ifndef CLIENT_H

#define CLIENT_H

#include "../Common/common.h"

#ifndef ACCESSPERMS

#define ACCESSPERMS(S_IRWXU | S_IRWXG | S_IRWXO

#endif

FileMetadata *filesToDelete;

FileMetadata *filesToUpdate;

int filesToUpdateCount;

int fileToDeleteCount;

char *formatdate(time_t val);

int adjustTimestamp(char *filepath, char *timestamp);

int getIndexFromServerFiles(FileMetadata *serverFiles, int count, char *path);

int removeDirectory(char *directoryPath);

int removeFile(char *path);

void getFilesToDelete(FileMetadata *serverFiles, int serverFilesCount;

void getFilesToUpdate(FileMetadata *serverFiles, int serverFilesCount);

#endif //COMMON_H

common.c

#include "common.h"

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <dirent.h>

#include <sys/stat.h>

int pathsCount=0;

void displayError(char *msg) {

fprintf(stderr, "%s:%s\n", msg, strerror(errno));

exit(errno);

}

int isEmptyDirectory(char *directoryPath) {

char cmd[1024];

int status, exitcode;

snprintf(cmd, 1024, "test $(ls -A \"%s\" 2>/dev/null | wc -l) -ne 0", directoryPath);

status = system(cmd);

exitcode = WEXITSTATUS(status);

return exitcode;

}

int getPathIndex(char *path) {

for (int i = 0; i < pathsCount; i++) {

if (!strcmp(ownFiles[i].path, path)) {

return i;

}

}

return -1;

}

void readConfigurationParameters(char *path) {

char errorMessage[PATH_MAX + 100];

FILE *f = fopen(path, "r");

if (NULL == f) {

snprintf(errorMessage, sizeof(errorMessage), "Error opening configuration file: %s\n", path);

displayError(errorMessage);

}

ip = (char *) malloc(20);

if (NULL == ip) {

snprintf(errorMessage, sizeof(errorMessage), "Configuration file: error at memory allocation for ip: %s\n", path);

displayError(errorMessage);

}

char *s_port = (char *) malloc(20);

if (NULL == s_port) {

snprintf(errorMessage, sizeof(errorMessage), "Configuration file: error at memory allocation for port: %s\n", path);

displayError(errorMessage);

}

errno = 0;

fscanf(f, "ip=%s\nport=%s", ip, s_port);

if (errno) {

snprintf(errorMessage, sizeof(errorMessage), "Error while reading from configuration file: %s\n", path);

displayError(errorMessage);

}

if (fclose(f)) {

snprintf(errorMessage, sizeof(errorMessage), "Error at closing configuration file: %s\n", path);

displayError(errorMessage);

}

port = atoi(s_port);

printf("[loaded] ip: %s, port: %d\n", ip, port);

}

void printPaths() {

for (int i = 0; i < pathsCount; i++)

printf("%s\n", paths[i]);

}

void getAllFilesPaths(char *directoryPath) {

getFilesPaths(directoryPath, strlen(directoryPath) + 1);

strcpy(paths[pathsCount], "");

}

void getFilesPaths(char *directoryPath, int length) {

DIR *currentDirectory;

struct dirent *currentEntrance;

char *entranceName;

struct stat entranceInfo;

char path[PATH_MAX];

char errorMessage[PATH_MAX + 100];

if (!(currentDirectory = opendir(directoryPath))) {

snprintf(errorMessage, sizeof(errorMessage), "Error while opening directory: %s\n", directoryPath);

displayError(errorMessage);

}

errno = 0;

while ((currentEntrance = readdir(currentDirectory)) > 0) {

entranceName = currentEntrance->d_name;

if (strcmp(entranceName, ".") == 0 || strcmp(entranceName, "..") == 0)

continue;

snprintf(path, sizeof(path), "%s/%s", directoryPath, entranceName);

if (lstat(path, &entranceInfo) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "Error at lstat: %s\n", path);

displayError(errorMessage);

}

strcpy(paths[pathsCount++], path + length);

if (S_ISDIR(entranceInfo.st_mode) && !isEmptyDirectory(path)) {

getFilesPaths(path, length);

}

}

if (errno) {

snprintf(errorMessage, sizeof(errorMessage), "Error while reading from directory: %s\n", directoryPath);

displayError(errorMessage);

}

if (closedir(currentDirectory)) {

snprintf(errorMessage, sizeof(errorMessage), "Error at closing directory: %s\n", directoryPath);

displayError(errorMessage);

}

}

void getAllFilesMetadata(char *root) {

char errorMessage[PATH_MAX + 100];

ownFiles = (FileMetadata *) malloc(pathsCount * sizeof(FileMetadata));

//pentru fiecare path din paths, creaza un nou obiect file_metadata,

//populeaza-l cu date apeland lstat SI adauga-l la own_files_metadata

if (NULL == ownFiles) {

displayError("Error at memory allocation for ownFiles");

}

struct stat entranceInfo;

for (int i = 0; i < pathsCount; i++) {

char *fullPath = (char *) malloc(strlen(root) + strlen(paths[i]) + 10);

if (NULL == fullPath) {

snprintf(errorMessage, sizeof(errorMessage), "Error at memory allocation for fullPath:");

displayError(errorMessage);

}

snprintf(fullPath, PATH_MAX, "%s/%s", root, paths[i]);

strcpy(ownFiles[i].path, paths[i]);

if (stat(fullPath, &entranceInfo) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "Error at stat: %s\n", fullPath);

displayError(errorMessage);

}

ownFiles[i].size = entranceInfo.st_size;

ownFiles[i].timeStamp = entranceInfo.st_mtime;

ownFiles[i].isRegularFile = S_ISDIR(entranceInfo.st_mode) ? 0 : 1;

if (fullPath) {

free(fullPath);

}

}

}

common.h

#ifndef COMMON_H

#define COMMON_H

#define DEBUG_MODE

#include <limits.h>

typedef struct FileMetadata {

char path[PATH_MAX];

unsigned int size;

unsigned long timeStamp;

short isRegularFile; // 1 if regular file; 0 if directory

} FileMetadata;

extern int pathsCount;

char paths[1024][PATH_MAX];

FileMetadata *ownFiles;

char *ip;

int port;

void displayError(char *msg);

int isEmptyDirectory(char *directoryPath);

int getPathIndex(char *path);

void readConfigurationParameters(char *path);

void printPaths();

void getAllFilesPaths(char *directoryPath);

void getFilesPaths(char *directoryPath, int length);

void getAllFilesMetadata(char *root);

#endif //COMMON_H

server.config

ip=127.0.0.1

port=9002

server.c

#include <unistd.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/wait.h>

#include "../Common/common.h"

#include "server.h"

int socketFileDescriptor;

struct sockaddr_in serverAddress, remoteAddress;

socklen_t len;

char *root;

char errorMessage[PATH_MAX + 100];

off_t getFileSize(char *path) {

struct stat entranceInfo;

if (lstat(path, &entranceInfo) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError at lstat: %s\n", path);

displayError(errorMessage);

}

return entranceInfo.st_size;

}

void handleRequest(int connectionFileDescriptor) {

char buff[PATH_MAX], newPath[PATH_MAX];

int n;

int filesToUpdateCount;

FileMetadata *filesToUpdate;

/*

Telling the client how many files are ready to be synchronized.

Then sending their files to check which of them needs sync.

*/

snprintf(buff, 10, "%d", pathsCount);

if (write(connectionFileDescriptor, buff, 10) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while writing path count from the server:\n");

displayError(errorMessage);

}

for (int i = 0; i < pathsCount; i++) {

if (write(connectionFileDescriptor, &ownFiles[i], sizeof(FileMetadata)) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while writing some FileMetadata from the server :\n");

displayError(errorMessage);

}

}

/*

Receiving the number of files that need updated on clients' computer.

*/

n = read(connectionFileDescriptor, buff, 10);

if (n < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while reading from connectionFileDescriptor");

displayError(errorMessage);

}

buff[n] = 0;

filesToUpdateCount = atoi(buff);

#if defined DEBUG_MODE

printf("[debug - server]: client needs %d files updated.\n", filesToUpdateCount);

#endif

/*

Allocate enough memory for the files.

*/

if ((filesToUpdate = (FileMetadata *) malloc(filesToUpdateCount * sizeof(FileMetadata))) == NULL) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while allocating memory to filesToUpdate :\n");

displayError(errorMessage);

}

/*

Store the files into an array.

*/

for (int i = 0; i < filesToUpdateCount; i++) {

n = read(connectionFileDescriptor, buff, PATH_MAX);

if (n < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while reading filesToUpdate");

displayError(errorMessage);

}

buff[n] = 0;

#if defined DEBUG_MODE

printf("[debug - server]: client needs file '%s' updated.\n", buff);

#endif

filesToUpdate[i] = ownFiles[getPathIndex(buff)];

}

/*

Building absolute path + transfer non-updated files to the client.

*/

for (int i = 0; i < filesToUpdateCount; i++) {

snprintf(newPath, PATH_MAX, "%s/%s", root, filesToUpdate[i].path);

#if defined DEBUG_MODE

printf("[debug - server]: file '%s' is updating to client.\n", newPath);

#endif

if (filesToUpdate[i].isRegularFile) // is not a directory

{

int fd;

int n;

if ((fd = open(newPath, O_RDONLY)) == -1) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while opening file :%s\n", newPath);

displayError(errorMessage);

}

off_t size = getFileSize(newPath);

#if defined DEBUG_MODE

printf("[debug - server]: file '%s', size=%ld\n", newPath, size);

#endif

snprintf(buff, 12, "%ld", size);

if(write(connectionFileDescriptor, buff, 12)<0){

snprintf(errorMessage, sizeof(errorMessage), "\nError while writing filesToUpdate stuff 2");

displayError(errorMessage);

}

n=read(fd, buff, size);

if(n<0){

snprintf(errorMessage, sizeof(errorMessage), "\nError while reading filesToUpdate stuff 2");

displayError(errorMessage);

}

if(write(connectionFileDescriptor, buff, size)<0){

snprintf(errorMessage, sizeof(errorMessage), "\nError while writing filesToUpdate stuff 3");

displayError(errorMessage);

}

if (close(fd) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while closing update file:%s\n",newPath);

displayError(errorMessage);

}

#if defined DEBUG_MODE

printf("[debug - server]: file '%s' successfully updated.\n", filesToUpdate[i].path);

#endif

}

}

// if(close(connectionFileDescriptor)<0){

// snprintf(errorMessage, sizeof(errorMessage), "\nError while closing connection file descriptor");

// displayError(errorMessage);

// }

exit(0);

}

void acc() {

int connfd;

pid_t wpid, pid;

int status;

len = sizeof(remoteAddress);

while ((connfd = accept(socketFileDescriptor, (struct sockaddr *) &remoteAddress, &len)) >= 0)

{

if ((pid = fork()) == 0)

handleRequest(connfd);

close(connfd);

while ((wpid = wait(&status)) > 0);

}

}

void serverSetup() {

if ((socketFileDescriptor = socket(PF_INET, SOCK_STREAM, 0)) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nError while server setup :\n");

displayError(errorMessage);

}

serverAddress.sin_family = AF_INET;

serverAddress.sin_addr.s_addr = inet_addr(ip);

serverAddress.sin_port = htons(port);

if (bind(socketFileDescriptor, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {

snprintf(errorMessage, sizeof(errorMessage), "\nBinding failure :\n");

displayError(errorMessage);

}

listen(socketFileDescriptor, 5);

getAllFilesPaths(root);

getAllFilesMetadata(root);

acc();

}

int main(int argc, char *args[]) {

if (argc != 3) {

printf("Usage %s <config_file_path> <root>.\n", args[0]);

exit(1);

}

// signal(SIGCHLD, SIG_IGN);

readConfigurationParameters(args[1]);

root = args[2];

serverSetup();

return 0;

}

server.h

#ifndef SERVER_H

#define SERVER_H

void handleRequest(int connectionFileDescriptor);

void acc();

void serverSetup();

#endif //SERVER_H