uni

University stuff
git clone git://git.margiolis.net/uni.git
Log | Files | Refs | README | LICENSE

commit a93b9c0ff9b184453a95cdde7edbe3700c5a4613
parent ba873087968796ad622f7968a39711ed974e7a5f
Author: Christos Margiolis <christos@FreeBSD.org>
Date:   Sun,  9 Jun 2024 23:58:54 +0200

foobar

Diffstat:
Aconcurrent_programming/extern.h | 15+++++++++++++++
Mconcurrent_programming/rps_client.c | 223++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mconcurrent_programming/rps_server.c | 207++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Djava_multimedia/VideoStreaming/.classpath | 15---------------
Djava_multimedia/VideoStreaming/.project | 17-----------------
Djava_multimedia/VideoStreaming/.settings/org.eclipse.core.resources.prefs | 2--
Djava_multimedia/VideoStreaming/.settings/org.eclipse.jdt.core.prefs | 14--------------
Djava_multimedia/VideoStreaming/bin/module-info.class | 0
Djava_multimedia/VideoStreaming/bin/video_streaming/Main.class | 0
Djava_multimedia/VideoStreaming/src/module-info.java | 7-------
Djava_multimedia/VideoStreaming/src/video_streaming/Main.java | 19-------------------
11 files changed, 365 insertions(+), 154 deletions(-)

diff --git a/concurrent_programming/extern.h b/concurrent_programming/extern.h @@ -0,0 +1,15 @@ +#ifndef _EXTERN_H_ +#define _EXTERN_H_ + +#define CMD_LEN 32 +#define NAME_LEN 32 +#define MOVE_TIMEOUT 3 +#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) + +enum { + MOVE_ROCK, + MOVE_PAPER, + MOVE_SCISSOR, +}; + +#endif /* _EXTERN_H_ */ diff --git a/concurrent_programming/rps_client.c b/concurrent_programming/rps_client.c @@ -1,4 +1,3 @@ -#include <err.h> #include <sys/socket.h> #include <sys/types.h> @@ -6,32 +5,34 @@ #include <netdb.h> #include <netinet/in.h> +#include <err.h> +#include <errno.h> #include <locale.h> -#include <ncurses.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) +#include "extern.h" struct command { - char name[32]; - void (*func)(char *); + char name[CMD_LEN]; + int (*func)(char *); }; -static void cmd_challenge(char *); -static void cmd_play(char *); -static void cmd_msg(char *); -static void cmd_list(char *); -static void cmd_quit(char *); -static void cmd_help(char *); -static int init_curses(void); +static int cmd_challenge(char *); +static int cmd_play(char *); +static int cmd_msg(char *); +static int cmd_list(char *); +static int cmd_quit(char *); +static int cmd_help(char *); static struct command *parse_command(char **); +static void sighandler(int); static void usage(void); static struct command commands[] = { - { "challenge", cmd_challenge }, /* /challenge <name|id> */ + { "challenge", cmd_challenge }, /* /challenge <id> */ { "play", cmd_play }, /* /play <rock|paper|scissor> */ { "msg", cmd_msg }, /* /msg <message> */ { "list", cmd_list }, /* /list */ @@ -44,38 +45,120 @@ static int xmax; static int f_quit = 0; static int fd; -static void +static int cmd_challenge(char *args) { + char cmd[CMD_LEN]; + int id; + + if (args == NULL) { + warnx("usage: /challenge <id>"); + return (-1); + } + + strlcpy(cmd, "challenge", sizeof(cmd)); + if (send(fd, cmd, sizeof(cmd), 0) < 0) { + warn("send(challenge, cmd)"); + return (-1); + } + + id = strtol(args, NULL, 10); + if (errno == EINVAL || errno == ERANGE) { + warn("strtol(%s)", args); + return (-1); + } + if (send(fd, &id, sizeof(id), 0) < 0) { + warn("send(challenge, id)"); + return (-1); + } + + return (0); } -static void +static int cmd_play(char *args) { - /* TODO add fuzzy parsing */ - /* TODO ask for command and send it to server */ + char cmd[CMD_LEN]; + int move; + + if (args == NULL) { + warnx("usage: /challenge <rock|paper|scissor>"); + return (-1); + } + + strlcpy(cmd, "play", sizeof(cmd)); + if (send(fd, cmd, sizeof(cmd), 0) < 0) { + warn("send(play, cmd)"); + return (-1); + } + + if (strcmp(args, "rock") == 0) + move = MOVE_ROCK; + else if (strcmp(args, "paper") == 0) + move = MOVE_PAPER; + else if (strcmp(args, "scissor") == 0) + move = MOVE_SCISSOR; + else + warnx("invalid move: %s\n", args); + + if (send(fd, &move, sizeof(move), 0) < 0) { + warn("send(play, move)"); + return (-1); + } + + return (0); } -static void +static int cmd_msg(char *args) { + char cmd[CMD_LEN]; + + if (args == NULL) { + warnx("usage: /msg <message>"); + return (-1); + } + + strlcpy(cmd, "msg", sizeof(cmd)); + if (send(fd, cmd, sizeof(cmd), 0) < 0) { + warn("send(msg)"); + return (-1); + } + + return (0); } -static void +static int cmd_list(char *args) { + char cmd[CMD_LEN]; + + strlcpy(cmd, "list", sizeof(cmd)); + if (send(fd, cmd, sizeof(cmd), 0) < 0) { + warn("send(list)"); + return (-1); + } + + return (0); } -/* TODO return int... */ -static void +static int cmd_quit(char *args) { + char cmd[CMD_LEN]; + f_quit = 1; - if (send(fd, &f_quit, sizeof(f_quit), 0) < 0) - ; /* TODO */ + + strlcpy(cmd, "quit", sizeof(cmd)); + if (send(fd, cmd, sizeof(cmd), 0) < 0) { + warn("send(quit)"); + return (-1); + } + + return (0); } -static void +static int cmd_help(char *args) { printf("Available commands:\n"); @@ -84,35 +167,6 @@ cmd_help(char *args) printf("/msg <message>\t\t\tSend a message to the global chat\n"); printf("/quit\t\t\t\tQuit the game\n"); printf("/help\t\t\t\tShow this help message\n"); -} - -static int -init_curses(void) -{ - if (!initscr()) - return (-1); - - /* Don't echo keypresses. */ - noecho(); - /* Disable line buffering. */ - cbreak(); - /* Hide the cursor. */ - curs_set(false); - /* Allow arrow-key usage. */ - keypad(stdscr, true); - /* ESC has a small delay after it's pressed. Remove it. */ - set_escdelay(0); - /* - * Don't wait for a keypress -- just continue if there's no keypress - * within 1000 milliseconds. We could set the delay to 0 milliseconds, - * but this will most likely burn the CPU. - */ - timeout(1000); - (void)getmaxyx(stdscr, ymax, xmax); - start_color(); - /* Use the terminal's colorscheme. */ - use_default_colors(); - /* TODO init pairs */ return (0); } @@ -156,6 +210,9 @@ parse_command(char **args) while (**args == ' ') (*args)++; } + /* FIXME */ + /*if (**args == '\0')*/ + /**args = NULL;*/ cmd = strtok(line, " "); if (cmd == NULL) { @@ -178,6 +235,12 @@ parse_command(char **args) } static void +sighandler(int sig) +{ + f_quit = 1; +} + +static void usage(void) { fprintf(stderr, "usage: %1$s [-p port] <hostname|ipv4_addr>\n", @@ -190,8 +253,10 @@ main(int argc, char *argv[]) { struct sockaddr_in sin; struct hostent *hp; + struct sigaction sa; struct command *cmd; - char *args; + char *args, *s; + char nick[NAME_LEN]; int port = 9999; int ch; @@ -211,6 +276,40 @@ main(int argc, char *argv[]) if (argc < 1) usage(); + /* + * Handle termination signals so we don't exit abnormally (i.e without + * cleaning up resources). + */ + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = sighandler; + if (sigaction(SIGINT, &sa, NULL) < 0) + err(1, "sigaction(SIGINT)"); + if (sigaction(SIGTERM, &sa, NULL) < 0) + err(1, "sigaction(SIGTERM)"); + + if (!setlocale(LC_ALL, "")) + err(1, "setlocale"); + + /* Choose nickname. */ + printf("\rEnter nickname (no whitespaces): "); + fgets(nick, sizeof(nick), stdin); + s = nick; + + if (*s == '\n' || *s == '\0') + errx(1, "empty nickname"); + + /* Get rid of leading whitespaces. */ + while (*s == ' ') + s++; + + /* Get rid of trailing newlines and whitespaces. */ + for (; *s != '\0'; s++) { + if (*s != '\n' && *s != ' ') + continue; + *s = '\0'; + break; + } if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err(1, "socket(AF_INET)"); @@ -225,13 +324,11 @@ main(int argc, char *argv[]) if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) err(1, "connect"); - /* TODO nickname selection (server keeps asking until unique) */ - - if (!setlocale(LC_ALL, "")) - err(1, "setlocale"); + /* Send nickname to server */ + if (send(fd, nick, sizeof(nick), 0) < 0) + err(1, "send(nick)"); - /*if (init_curses() < 0)*/ - /*errx(1, "could not initialize ncurses");*/ + cmd_list(NULL); for (;;) { if (f_quit) @@ -239,14 +336,12 @@ main(int argc, char *argv[]) /* TODO redraw */ if ((cmd = parse_command(&args)) == NULL) continue; - /* TODO connect to server */ - cmd->func(args); + if (cmd->func(args) < 0) + warnx("/%s(%s) failed\n", cmd->name, args); if (args != NULL) free(args); } close(fd); - /*(void)endwin();*/ - return (0); } diff --git a/concurrent_programming/rps_server.c b/concurrent_programming/rps_server.c @@ -1,5 +1,8 @@ +#include <sys/mman.h> +#include <sys/queue.h> #include <sys/socket.h> #include <sys/types.h> +#include <sys/wait.h> #include <arpa/inet.h> #include <netdb.h> @@ -12,27 +15,161 @@ #include <string.h> #include <unistd.h> -static int srv(int); +#include "extern.h" + +struct user { + int fd; + int id; + char name[NAME_LEN]; + int move; + int nplays; + int nwins; + struct user *opponent; + TAILQ_ENTRY(user) next; +}; + +static int srv(struct user *); static void sighandler(int); static void usage(void); static volatile sig_atomic_t f_quit = 0; +static int ids = 0; +static int *nusers; +static TAILQ_HEAD(, user) users = TAILQ_HEAD_INITIALIZER(users); + +static void +remove_user(struct user *user) +{ + struct user *up; + + while (!TAILQ_EMPTY(&users)) { + up = TAILQ_FIRST(&users); + TAILQ_REMOVE(&users, up, next); + close(up->fd); + munmap(up, sizeof(struct user)); + break; + } +} static int -srv(int fd) +srv(struct user *user) { - int quit; + struct user *up; + char cmd[CMD_LEN]; + int rc = 0, tmp, quit, id; for (;;) { - if (recv(fd, &quit, sizeof(quit), 0) < 0) + tmp = recv(user->fd, cmd, sizeof(cmd), 0); + if (tmp < 0) { + warn("recv(%d, cmd)", user->fd); + rc = -1; break; - if (quit) { - printf("%s(): quit triggered\n", __func__); + } else if (tmp == 0) break; + + if (strcmp(cmd, "challenge") == 0) { + if (recv(user->fd, &id, sizeof(id), 0) < 0) { + warn("recv(%d, id)", user->fd); + rc = -1; + break; + } + up = NULL; + TAILQ_FOREACH(up, &users, next) { + if (up->id == id) + break; + } + if (up == NULL) { + warnx("id=%d not found", id); + continue; + } + if (up->opponent != NULL) { + warnx("%s is already challenged", up->name); + continue; + } + user->opponent = up; + up->opponent = user; + printf("%s is challenging %d\n", user->name, id); + /* TODO challenge available user */ + } else if (strcmp(cmd, "play") == 0) { + if (recv(user->fd, &user->move, + sizeof(user->move), 0) < 0) { + warn("recv(%d, play)", user->fd); + rc = -1; + break; + } + + /* Cannot play when unchallenged. */ + if (user->opponent == NULL) + continue; + + /* Give the opponent 3 seconds to make a move. */ + for (int i = 0; user->opponent->move < 0 && + i < MOVE_TIMEOUT; i++) { + printf("[%s - %s] %s sleeping\n", + user->name, user->opponent->name, user->name); + sleep(1); + } + + /* Didn't respond, he lost. */ + if (user->opponent->move < 0) { + printf("[%s - %s] opponent didn't respond\n", + user->name, user->opponent->name); + user->move = -1; + user->opponent = NULL; + /*user->opponent->opponent = NULL;*/ + user->nplays++; + user->nwins++; + } + + printf("%s=%d, %s=%d\n", user->name, user->move, + user->opponent->name, user->opponent->move); + + switch (user->move) { + case MOVE_ROCK: + if (user->opponent->move == MOVE_SCISSOR) + user->nwins++; + break; + case MOVE_PAPER: + if (user->opponent->move == MOVE_ROCK) + user->nwins++; + break; + case MOVE_SCISSOR: + if (user->opponent->move == MOVE_PAPER) + user->nwins++; + break; + default: + warnx("invalid move: %d", user->move); + continue; + } + printf("[%s - %s] = [%d - %d]\n", + user->name, user->opponent->name, + user->nwins, user->opponent->nwins); + + user->move = -1; + user->opponent = NULL; + user->opponent->opponent = NULL; + user->nplays++; + } else if (strcmp(cmd, "msg") == 0) { + /* TODO post message to global chat (list) */ + } else if (strcmp(cmd, "list") == 0) { + printf("ID\tNAME\tPLAYED\tWON\n"); + TAILQ_FOREACH(up, &users, next) { + printf("%d\t%s\t%d\t%d\n", + up->id, up->name, up->nplays, up->nwins); + } + /* TODO send as nvlist */ + } else if (strcmp(cmd, "quit") == 0) { + break; + } else { + warnx("received unknown command: %s", cmd); } } - return (0); + printf("%s: client disconnected: %s#%d (fd=%d)\n", + getprogname(), user->name, user->id, user->fd); + remove_user(user); + + return (rc); } static void @@ -55,10 +192,10 @@ main(int argc, char *argv[]) struct sockaddr_in sin; struct hostent *hp; struct sigaction sa; + struct user *user; int backlog = 10; int port = 9999; int sfd; - int cfd; int ch; while ((ch = getopt(argc, argv, "b:p:")) != -1) { @@ -136,13 +273,52 @@ main(int argc, char *argv[]) if (listen(sfd, backlog) < 0) err(1, "listen"); + /* mmap(2) this that we can modify decrement it from the child. */ + nusers = mmap(NULL, sizeof(*nusers), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (nusers == MAP_FAILED) + err(1, "mmap"); + *nusers = 0; + + TAILQ_INIT(&users); + for (;;) { /* We caught a termination signal. */ if (f_quit) break; - if ((cfd = accept(sfd, NULL, NULL)) < 0) + + /* + * Allocate user structure using mmap(2) so that we can modify + * the userlist from the child process. + */ + user = mmap(NULL, sizeof(struct user), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (user == MAP_FAILED) + err(1, "mmap"); + + if ((user->fd = accept(sfd, NULL, NULL)) < 0) { + warn("accept(%d)", user->fd); + remove_user(user); + continue; + } + /* Receive nickname and assign ID to user. */ + if (recv(user->fd, user->name, sizeof(user->name), 0) < 0) { + warn("recv(%d, nick)", user->fd); + remove_user(user); continue; - printf("[%s] client connected: %d\n", getprogname(), cfd); + } + + user->id = ++ids; + user->nplays = 0; + user->nwins = 0; + user->opponent = NULL; + TAILQ_INSERT_TAIL(&users, user, next); + + (*nusers)++; + + printf("%s: active users=%d\n", getprogname(), *nusers); + printf("%s: client connected: %s#%d (fd=%d)\n", + getprogname(), user->name, user->id, user->fd); /* * Create a child process to serve the client so the parent can * continue waiting for another client to serve. @@ -151,19 +327,18 @@ main(int argc, char *argv[]) case -1: err(1, "fork"); case 0: - if (srv(cfd) < 0) - warnx("srv failed"); - printf("[%s] client disconnected: %d\n", - getprogname(), cfd); + if (srv(user) < 0) + warnx("srv(%s#%d) failed", user->name, user->id); + (*nusers)--; _exit(0); default: - close(cfd); + close(user->fd); } } /* Will get here only if a termination signal is caught. */ close(sfd); - close(cfd); + remove_user(user); return (0); } diff --git a/java_multimedia/VideoStreaming/.classpath b/java_multimedia/VideoStreaming/.classpath @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JavaFX"> - <attributes> - <attribute name="module" value="true"/> - </attributes> - </classpathentry> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"> - <attributes> - <attribute name="module" value="true"/> - </attributes> - </classpathentry> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/java_multimedia/VideoStreaming/.project b/java_multimedia/VideoStreaming/.project @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>VideoStreaming</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/java_multimedia/VideoStreaming/.settings/org.eclipse.core.resources.prefs b/java_multimedia/VideoStreaming/.settings/org.eclipse.core.resources.prefs @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -encoding/<project>=UTF-8 diff --git a/java_multimedia/VideoStreaming/.settings/org.eclipse.jdt.core.prefs b/java_multimedia/VideoStreaming/.settings/org.eclipse.jdt.core.prefs @@ -1,14 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=11 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=11 diff --git a/java_multimedia/VideoStreaming/bin/module-info.class b/java_multimedia/VideoStreaming/bin/module-info.class Binary files differ. diff --git a/java_multimedia/VideoStreaming/bin/video_streaming/Main.class b/java_multimedia/VideoStreaming/bin/video_streaming/Main.class Binary files differ. diff --git a/java_multimedia/VideoStreaming/src/module-info.java b/java_multimedia/VideoStreaming/src/module-info.java @@ -1,6 +0,0 @@ -module video_streaming { - exports video_streaming; - requires transitive javafx.controls; - requires javafx.fxml; - requires javafx.graphics; -} -\ No newline at end of file diff --git a/java_multimedia/VideoStreaming/src/video_streaming/Main.java b/java_multimedia/VideoStreaming/src/video_streaming/Main.java @@ -1,19 +0,0 @@ -package video_streaming; - -import javafx.application.Application; -import javafx.scene.Scene; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; - -public class Main extends Application { - @Override - public void start(Stage stg) throws Exception { - stg.setTitle("Video Streaming Client"); - stg.setScene(new Scene(new StackPane(), 350, 230)); - stg.show(); - } - - public static void main(String[] args) { - launch(args); - } -}