uni

University stuff
git clone git://git.christosmarg.xyz/uni-assignments.git
Log | Files | Refs | README | LICENSE

ex3_server.c (5084B)


      1 #include <sys/socket.h>
      2 #include <sys/types.h>
      3 #include <sys/un.h>
      4 
      5 #include <err.h>
      6 #include <signal.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <unistd.h>
     11 
     12 /*
     13  * Εργαστήριο ΛΣ2 (Δ6) / Εργασία 2: Άσκηση 3 (server) / 2020-2021
     14  * Ονοματεπώνυμο: Χρήστος Μαργιώλης
     15  * ΑΜ: 19390133
     16  * Τρόπος μεταγλώττισης: `cc ex3_server.c -o ex3_server`
     17  */
     18 
     19 /* Results to be sent back to the client. */
     20 struct pack_res {
     21 	char str[32];
     22 	float avg;
     23 };
     24 
     25 struct foo {
     26 	int cfd;
     27 	int nsucc;
     28 	int ntotal;
     29 };
     30 
     31 static int srv(struct foo *);
     32 static void sighandler(int);
     33 static void *emalloc(size_t);
     34 
     35 static char *argv0;
     36 /* Only gets set if a termination signal is caught. */
     37 static volatile sig_atomic_t f_quit = 0;
     38 
     39 static int
     40 srv(struct foo *foo)
     41 {
     42 	struct foo *f;
     43 	struct pack_res *res;
     44 	int *arr;
     45 	int i, n, sum;
     46 	int rc;
     47 	char cont;
     48 
     49 	/*
     50 	 * The return code is -1 (error) by default so that we don't
     51 	 * need to set it everytime an error occurs in the main loop.
     52 	 * If there are no errors, `rc` will be set to 0 (success) right
     53 	 * after the loop.
     54 	 */
     55 	rc = -1;
     56 	f = (struct foo *)foo;
     57 	for (;;) {
     58 		if (recv(f->cfd, &n, sizeof(int), 0) < 0)
     59 			goto fail;
     60 		arr = emalloc(n * sizeof(int));
     61 		if (recv(f->cfd, arr, n * sizeof(int), 0) < 0)
     62 			goto fail;
     63 		res = emalloc(sizeof(struct pack_res));
     64 		printf("cfd: %d\tn: %d\n", f->cfd, n);
     65 		for (i = 0, sum = 0; i < n; i++) {
     66 			printf("cfd: %d\tarr[%d]: %d\n", f->cfd, i, arr[i]);
     67 			sum += arr[i];
     68 		}
     69 		free(arr);
     70 		/* 
     71 		 * If we go to `fail:`, `gcc` for some reason double frees if 
     72 		 * we don't manually set it to NULL, even though we explicitly 
     73 		 * check for NULL first...
     74 		 */
     75 		arr = NULL;
     76 
     77 		if ((res->avg = sum / (float)n) > 10.0f) {
     78 			(void)strncpy(res->str, "sequence: ok",
     79 			    sizeof(res->str) - 1);
     80 			f->nsucc++;
     81 		} else
     82 			(void)strncpy(res->str, "sequence: failed",
     83 			    sizeof(res->str) - 1);
     84 		if (send(f->cfd, res, sizeof(struct pack_res), 0) < 0)
     85 			goto fail;
     86 		f->ntotal++;
     87 		printf("[%s] success: %d: total: %d\n",
     88 		    argv0, f->nsucc, f->ntotal);
     89 
     90 		if (recv(f->cfd, &cont, 1, 0) < 0)
     91 			goto fail;
     92 		if (cont == 'n')
     93 			break;
     94 	}
     95 	rc = 0;
     96 
     97 fail:
     98 	printf("[%s] connection with client %d closed\n", argv0, f->cfd);
     99 	(void)close(f->cfd);
    100 	if (arr != NULL)
    101 		free(arr);
    102 	if (res != NULL)
    103 		free(res);
    104 
    105 	return rc;
    106 }
    107 
    108 static void
    109 sighandler(int sig)
    110 {
    111 	f_quit = 1;
    112 }
    113 
    114 static void *
    115 emalloc(size_t nb)
    116 {
    117 	void *p;
    118 
    119 	if ((p = malloc(nb)) == NULL)
    120 		err(1, "malloc");
    121 	return p;
    122 }
    123 
    124 static void
    125 usage(void)
    126 {
    127 	fprintf(stderr, "usage: %s [-b backlog] [-s sockfile]\n", argv0);
    128 	exit(1);
    129 }
    130 
    131 int
    132 main(int argc, char *argv[])
    133 {
    134 	struct foo *f;
    135 	struct sockaddr_un sun;
    136 	struct sigaction sa;
    137 	char *sockfile = "/tmp/cool.sock";
    138 	int sfd;
    139 	int backlog = 10;
    140 	char ch;
    141 
    142 	argv0 = *argv;
    143 	if ((ch = getopt(argc, argv, "b:s:")) != -1) {
    144 		switch (ch) {
    145 		case 'b':
    146 			/* 
    147 			 * Negative `backlog` value normally requests the
    148 			 * maximum allowable value (HISTORY section of
    149 			 * listen(2)'s FreeBSD man page), but it's better to
    150 			 * not allow it in case the user passes a negative
    151 			 * value accidentally. Also a value of 0 doesn't make
    152 			 * sense, so we don't allow it either.
    153 			 */
    154 			if ((backlog = atoi(optarg)) < 1)
    155 				usage();
    156 			break;
    157 		case 's':
    158 			sockfile = optarg;
    159 			break;
    160 		case '?':
    161 		default:
    162 			usage();
    163 		}
    164 	}
    165 	argc -= optind;
    166 	argv += optind;
    167 
    168 	/*
    169 	 * Handle termination signals so we don't exit abnormally
    170 	 * (i.e without cleaning up resources).
    171 	 */
    172 	(void)memset(&sa, 0, sizeof(sa));
    173 	(void)sigemptyset(&sa.sa_mask);
    174 	sa.sa_handler = sighandler;
    175 	sa.sa_flags = SA_RESTART;
    176 	/*if (sigaction(SIGHUP, &sa, NULL) < 0)*/
    177 		/*err(1, "sigaction: SIGHUP");*/
    178 	/*if (sigaction(SIGINT, &sa, NULL) < 0)*/
    179 		/*err(1, "sigaction: SIGINT");*/
    180 	/*if (sigaction(SIGTERM, &sa, NULL) < 0)*/
    181 		/*err(1, "sigaction: SIGTERM");*/
    182 
    183 	if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
    184 		err(1, "socket");
    185 	(void)memset(&sun, 0, sizeof(sun));
    186 	sun.sun_family = AF_UNIX;
    187 	(void)strncpy(sun.sun_path, sockfile, sizeof(sun.sun_path) - 1);
    188 
    189 	if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
    190 		err(1, "bind");
    191 	if (listen(sfd, backlog) < 0)
    192 		err(1, "listen");
    193 
    194 	f = emalloc(sizeof(struct foo));
    195 	f->nsucc = 0;
    196 	f->ntotal = 0;
    197 
    198 	for (;;) {
    199 		/* FIXME: blocked by accept(2) */
    200 		if (f_quit)
    201 			break;
    202 		/*
    203 		 * accept(2)'s `addr` and `addrlen` arguments can be NULL if
    204 		 * we don't care about the address information of the client.
    205 		 */
    206 		if ((f->cfd = accept(sfd, NULL, NULL)) < 0)
    207 			continue;
    208 		printf("[%s] accepted client: %d\n", argv0, f->cfd);
    209 		switch (fork()) {
    210 		case -1:
    211 			err(1, "fork");
    212 		case 0:
    213 			if (srv(f) < 0)
    214 				warnx("srv failed");
    215 			_exit(0);
    216 		default:
    217 			(void)close(f->cfd);
    218 		}
    219 
    220 	}
    221 
    222 	/* Will get here only if a termination signal is caught. */
    223 	(void)close(f->cfd);
    224 	(void)close(sfd);
    225 	free(f);
    226 	/*
    227 	 * bind(2)'s man page states that the socket should be deleted when
    228 	 * it's no longer needed, otherwise it'll stay there even after
    229 	 * we exit.
    230 	 */
    231 	(void)unlink(sockfile);
    232 
    233 	return 0;
    234 }