#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <wait.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include "array.h"
#include "process.h"
#include "builtins.h"
char *get_current_dir_name(void);
#define BUF_SIZE 2048
process *processes;
int write_all(int target_sock, char *buffer, size_t size) {
int nwrite, sent = 0;
while (sent < size) {
if ((nwrite = write(target_sock, buffer + sent, size - sent)) < 0) {
perror("Write");
printf("\n");
return -1;
}
sent += nwrite;
}
return sent;
}
void receive_file(int recv_sock, char *path, int size) {
char buffer[BUF_SIZE];
int file_fd;
int received;
if ((file_fd = open(path, (O_WRONLY | O_CREAT | O_TRUNC), 0644)) == -1) {
perror("Open");
printf("\n");
}
if (size == 0) {
// just touch
close(file_fd);
return;
}
received = 0;
/* Read all data */
while (received < size) {
int nread;
if ((nread = read(recv_sock, buffer, BUF_SIZE)) > 0) {
if (write_all(file_fd, buffer, nread) != nread) {
break;
}
received += nread;
}
}
if (received == size) {
printf("successfully wrote to remote file \"%s/%s\"\n", get_current_dir_name(), path);
printf("\n");
} else {
printf("error writing remote file \"%s/%s\"\n", get_current_dir_name(), path);
printf("\n");
}
close(file_fd);
}
void send_file(int client_fd, char *path) {
printf("Trying to get \"%s\" from remote...\n", path);
char buffer[BUF_SIZE];
memset(buffer, 0, BUF_SIZE);
int tmp_fd, file_fd;
if ((tmp_fd = open(path, O_RDONLY)) == -1) {
perror("Open");
printf("\n");
return;
}
FILE *file = fdopen(tmp_fd, "r");
fseek(file, 0L, SEEK_END);
long int sz = ftell(file);
fseek(file, 0L, SEEK_SET);
fclose(file);
close(tmp_fd);
if ((file_fd = open(path, O_RDONLY)) == -1) {
perror("Open");
printf("\n");
return;
}
int flag = 1;
// force flush
flag = 1;
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
flag = 0;
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
int length = snprintf(NULL, 0, "%ld", sz);
sprintf(buffer, "<<!%ld!", sz);
sprintf(&(buffer[3 + length + 1]), "%s!\n", path);
usleep(150);
if (write(client_fd, buffer, strlen(buffer)) != strlen(buffer)) {
perror("write header");
return;
}
usleep(150);
flag = 1;
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
usleep(150);
flag = 0;
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
usleep(150);
memset(buffer, 0, BUF_SIZE);
errno = 0;
size_t count;
while ((count = read(file_fd, buffer, sizeof(buffer))) > 0) {
/*printf("Sent %i bytes\n", */write_all(client_fd, buffer, count)/*)*/;
}
if (errno) {
perror("wad");
printf("\n");
}
close(file_fd);
//printf("done.\n");
}
void signal_handler(int signal) {
if (signal == SIGINT) {
for (size_t i = 0; i < arrayLen(processes); ++i) {
pid_t pid = processes[i].pid;
if (pid != 0) {
kill(pid, SIGINT);
}
}
}
}
bool check_put(char *buffer, int sock) {
// "<<!bytecount!name"
char cmd[4];
memcpy(cmd, buffer, 3);
cmd[3] = 0;
if (strcmp(cmd, "<<!") == 0) {
char rest[BUF_SIZE - 3];
size_t file_size;
int offset = 3;
int i = offset;
for (; i < BUF_SIZE; ++i) {
if (buffer[i] == '!')
break;
rest[i - offset] = buffer[i];
}
rest[i - offset] = 0;
file_size = atoi(rest);
offset = ++i;
for (; i < BUF_SIZE; ++i) {
if (buffer[i] == '!')
break;
rest[i - offset] = buffer[i];
}
rest[i - offset] = 0;
char path[strlen(rest) + 1];
memcpy(path, rest, strlen(rest));
path[strlen(rest)] = 0;
receive_file(sock, path, file_size);
return true;
}
return false;
}
int shell(int in_fd) {
int stdoutCopy = dup(STDOUT_FILENO);
int stderrCopy = dup(STDERR_FILENO);
dup2(in_fd, STDOUT_FILENO);
dup2(in_fd, STDERR_FILENO);
printf("Welcome! Available built-ins are:\n"
"cd: `cd <path>` - if no path is given, return to the current dir\n"
"wait: `wait pid1 ... pidN` - wait on the processes and report their exit conditions\n"
"fg: `fg pid` - pulls a process from the background back in the foreground\n"
"\n"
"You can put processes in the background using `&`\n"
"And with `|` you can pipe the output from one process to the input of another"
);
printf("\n$> ");
char const *const original_wd = get_current_dir_name();
//char const *prompt = relative_path(original_wd, original_wd);
bool done = false;
while (!done) {
char line[BUF_SIZE];
__ssize_t length;
fflush(stdout);
if ((length = read(in_fd, line, BUF_SIZE)) < 0) {
fprintf(stderr, "Failed to read from STDIN");
fflush(stderr);
exit(-1);
}
if (strspn(line, " \n\t") == strlen(line)) {
// skip empty lines - empty being just spaces or tabs
continue;
}
line[length - 1] = '\0'; // cut the line feed
if (strlen(line) == 0) {
continue;
}
if (check_put(line, in_fd))
continue;
processes = NULL;
parse_line(line, &processes);
if (strcmp(processes[0].argv[0], "cd") == 0) {
if (arrayLen(processes) != 1) {
perror("Can't chain cd with other processes");
printf("\n");
}
int ret;
switch (arrayLen(processes[0].argv)) {
case 3:
ret = chdir(processes[0].argv[1]);
break;
case 2:
ret = chdir(original_wd);
break;
default:
fprintf(stderr, "usage: cd <path>");
fprintf(stderr, "\n");
fflush(stderr);
ret = -1;
}
if (ret)
printf("[%i] ", ret);
else {
printf("%s", get_current_dir_name());
printf("\n");
}
} else if (strcmp(processes[0].argv[0], "exit") == 0) {
done = true;
} else if (strcmp(processes[0].argv[0], "get") == 0) {
if (arrayLen(processes[0].argv) != 3) {
printf("please provide a file name to get!");
printf("\n");
} else {
send_file(in_fd, processes[0].argv[1]);
}
} else if (strcmp(processes[0].argv[0], "wait") == 0) {
builtin_wait(processes[0], false);
} else if (strcmp(processes[0].argv[0], "fg") == 0) {
// same behaviour as wait, just bind to shell again (i.e. terminate process on ctrl-c)
builtin_wait(processes[0], true);
} else {
if (arrayLen(processes) != 0 && processes[arrayLen(processes) - 1].blocking) {
signal(SIGINT, signal_handler);
}
for (size_t i = 0; i < arrayLen(processes); ++i) {
/*int ret = */exec_command(processes[i]);
/*if (ret)
printf("[%i] ", ret);*/
}
printf("\n");
}
free_processes(&processes);
}
// reset output
dup2(STDOUT_FILENO, stdoutCopy);
dup2(STDERR_FILENO, stderrCopy);
free((void *) original_wd);
return 0;
}