// Experiment 13 / 15: TCP client-server program for COMMAND EXECUTION
// SERVER side - executes shell command sent by client, returns output
// Compile: gcc 13_tcp_cmd_exec_server.c -o cmdserver
// Run    : ./cmdserver

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 5555
#define BUF  4096

int main() {
    int server_fd, client_fd;
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    char cmd[256], output[BUF];

    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) { perror("socket"); exit(1); }

    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port        = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind"); exit(1);
    }
    listen(server_fd, 1);
    printf("Command-exec server listening on port %d...\n", PORT);

    client_fd = accept(server_fd, (struct sockaddr*)&addr, &addrlen);
    printf("Client connected.\n");

    while (1) {
        memset(cmd, 0, sizeof(cmd));
        int n = read(client_fd, cmd, sizeof(cmd) - 1);
        if (n <= 0) break;
        cmd[strcspn(cmd, "\n")] = '\0';
        if (strncmp(cmd, "exit", 4) == 0) break;

        printf("Executing: %s\n", cmd);

        // popen runs shell command and lets us read its output
        FILE *fp = popen(cmd, "r");
        if (!fp) {
            const char *err = "Error executing command\n";
            send(client_fd, err, strlen(err), 0);
            continue;
        }

        memset(output, 0, BUF);
        size_t total = 0;
        size_t got;
        while ((got = fread(output + total, 1, BUF - 1 - total, fp)) > 0) {
            total += got;
            if (total >= BUF - 1) break;
        }
        pclose(fp);

        if (total == 0) {
            const char *e = "(no output)\n";
            send(client_fd, e, strlen(e), 0);
        } else {
            send(client_fd, output, total, 0);
        }
    }

    close(client_fd);
    close(server_fd);
    return 0;
}
