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 }