-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlistener.c
329 lines (287 loc) · 10.5 KB
/
listener.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/**
* @file listener.c
* @brief Implementazione delle funzioni dichiarate in listener.h
* @author Emilio Panti 531844
* Si dichiara che il contenuto di questo file e' in ogni sua parte opera
* originale dell'autore
*/
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <signal.h>
#include <error_handler.h>
#include <connections.h>
#include <listener.h>
//configurazioni del server (definita in chatty.c)
extern configs_t conf_server;
/* --------------------------- funzioni di utilita' ------------------------------- */
/**
* @function updatemax
* @brief aggiorna il massimo id dei descrittori in set
*
* @param set dove sono registrati i fd da analizzare
* @param fdmax valore da aggiornare
*
* @return fdmax aggiornato, -1 se set vuoto
*/
static int updatemax(fd_set set, int fdmax) {
for(int i=(fdmax-1);i>=0;--i)
if (FD_ISSET(i, &set)) return i;
return -1;
}
/**
* @function close_all_fd
* @brief chiude tutti i fd presenti in set ad eccezione di quello destinato alla pipe
* (il descrittore della pipe è chiuso dalla funzione clean_all in chatty.c)
*
* @param set dove sono registrati i fd da chiudere
* @param fdmax fd di valore più alto presente in set
* @param pipe_fd descrittore della pipe (da non chiudere)
*/
static void close_all_fd(fd_set set, int fdmax, int pipe_fd) {
for(int i=0; i <= fdmax; i++) {
if (FD_ISSET(i, &set) && (i != pipe_fd)) close(i);
}
}
/**
* @function quit_fun
* @brief Invia un segnale al thread signal_handler (per comunicare che si deve
* terminare il processo) e chiama la pthread_exit()
*
* @param sh_tid tid del thread segnal_handler
*
* @note si presuppone che venga chiamata a seguito di un errore e che
* quindi errno sia settato
*/
static void quit_fun(pthread_t sh_tid) {
//se non riesco ad inviare il segnale al segnal_handler termino in maniera piu brutale
if (pthread_kill(sh_tid, SIGUSR2) != 0) exit(EXIT_FAILURE);
long ret = errno;
pthread_exit((void *) ret);
}
/* ------------------------- interfaccia listener ---------------------------- */
/**
* @function listener
* @brief Funzione eseguita dal thread listener
*
* @param arg argomenti necessari al thread listener
*
* @return 0 in caso di successo, altrimenti err
*/
void* listener(void* arg){
//prendo gli argomenti passati alla funzione
fd_queue_t *fd_queue = ((args_listener_t*)arg)->fd_queue;
pipe_fd_t *pipe_fd = ((args_listener_t*)arg)->pipe_fd;
pthread_t tid_sh = ((args_listener_t*)arg)->tid_sh;
char *socket_name = conf_server.socket_path;
int max_conn = conf_server.max_conn;
//creo il socket per ascoltare nuove connessioni
int listenfd = -1;
listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (listenfd == -1) quit_fun(tid_sh);
struct sockaddr_un serv_addr;
serv_addr.sun_family = AF_UNIX;
strncpy(serv_addr.sun_path, socket_name, strlen(socket_name)+1);
int check = 0;
check = bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr));
if (check == -1) {
close(listenfd);
quit_fun(tid_sh);
}
check = listen(listenfd, MAXBACKLOG);
if (check == -1) {
close(listenfd);
quit_fun(tid_sh);
}
fd_set set, tmpset;
// azzero sia il master set che il set temporaneo usato per la select
FD_ZERO(&set);
FD_ZERO(&tmpset);
// aggiungo il listener fd ed il descrittore in lettura della pipe al master set
FD_SET(listenfd, &set);
FD_SET(pipe_fd->pipe_read, &set);
// tengo traccia del file descriptor con id piu' grande
int fdmax;
if(listenfd > pipe_fd->pipe_read) fdmax = listenfd;
else fdmax = pipe_fd->pipe_read;
//numero di client connessi (non deve superare max_conn)
int n_conn = 0;
//ack da inviare al client se la connessione viene accettata
int ack = 1;
while(1) {
// copio il set nella variabile temporanea per la select
tmpset = set;
check = select(fdmax+1, &tmpset, NULL, NULL, NULL);
if (check == -1) {
close_all_fd(set, fdmax, pipe_fd->pipe_read);
quit_fun(tid_sh);
}
// cerchiamo di capire da quale fd abbiamo ricevuto una richiesta
for(int i=0; i <= fdmax; i++) {
if (FD_ISSET(i, &tmpset)) {
int connfd;
//se e' una nuova richiesta di connessione
if (i == listenfd) {
connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL);
if (connfd == -1) {
fprintf(stderr, "Errore: accept in listener\n");
close(connfd);
close_all_fd(set, fdmax, pipe_fd->pipe_read);
quit_fun(tid_sh);
}
//se non ho raggiunto max connessioni
if(n_conn < max_conn) {
//invio l'ack per comunicare al client che ho accettato la connessione
errno = 0;
check = writen(connfd, &ack, sizeof(int));
//se il client si è disconnesso
if (check == -1 && errno == EPIPE) {
close(connfd);
}
//errore
else if (check == -1 && errno != EPIPE) {
fprintf(stderr, "Errore: write ack in listener\n");
perror("Errno");
close(connfd);
close_all_fd(set, fdmax, pipe_fd->pipe_read);
quit_fun(tid_sh);
}
//se ok aggiungo l'fd al set e aggiorno le connessioni attuali
else {
FD_SET(connfd, &set);
n_conn++;
if(connfd > fdmax) fdmax = connfd;
}
}
//se ho raggiunto max connessioni
else {
close(connfd);
}
}
//se è una comunicazione sulla pipe
else if (i == pipe_fd->pipe_read) {
op_pipe_t op;
//leggo la comunicazione dalla pipe
check = read_pipe(pipe_fd, &connfd, &op);
if (check != 0) {
close_all_fd(set, fdmax, pipe_fd->pipe_read);
quit_fun(tid_sh);
}
//in base all' operazione letta
switch(op) {
//se è stata soddisfatta la richiesta del client
case UPDATE:
FD_SET(connfd, &set);
if(connfd > fdmax) fdmax = connfd;
break;
//se si è disconnesso il client durante l'esecuzione della sua richiesta
case CLOSE:
if (!FD_ISSET(connfd, &set)) {
n_conn--;
close(connfd);
}
break;
//se devo terminare il listener thread
case TERMINATE:
//chiudo tutti i descrittori aperti
close_all_fd(set, fdmax, pipe_fd->pipe_read);
//termino l'esecuzione
pthread_exit((void *) 0);
break;
default:
;
break;
}
}
//se un client già connesso è pronto a fare una nuova richiesta
else {
connfd = i;
//rimuovo l'fd dal set in attesa che venga eseguita la sua richiesta da un worker
FD_CLR(connfd, &set);
//aggiorno fdmax
if (connfd == fdmax) fdmax = updatemax(set, fdmax);
//aggiungo l'fd del client alla coda degli fd pronti
if (push_fd(fd_queue, connfd) == -1) {
close_all_fd(set, fdmax, pipe_fd->pipe_read);
quit_fun(tid_sh);
}
}
}
}
}
return 0;
}
/**
* @function starts_listener
* @brief Fa partire il listener thread
*
* @param fd_queue coda degli fd pronti
* @param pipe_fd gestore della pipe
* @param tid_sh tid del signal handler
*
* @return gestore listener se successo, NULL in caso di errore (errno settato)
*/
handler_listener_t* starts_listener(fd_queue_t *fd_queue, pipe_fd_t *pipe_fd, pthread_t tid_sh){
//controllo gli argomenti
err_check_return(fd_queue == NULL, EINVAL, "starts_listener", NULL);
err_check_return(pipe_fd == NULL, EINVAL, "starts_listener", NULL);
//creo il gestore listener
handler_listener_t *hl = malloc(sizeof(handler_listener_t));
err_return_msg(hl,NULL,NULL,"Errore: malloc\n");
(hl->arg).fd_queue = fd_queue;
(hl->arg).pipe_fd = pipe_fd;
(hl->arg).tid_sh = tid_sh;
//avvio il thread
int check = pthread_create(&(hl->tid), NULL, listener, &(hl->arg));
if (check != 0) {
errno = check;
fprintf(stderr, "pthread_create listener fallita\n");
free(hl);
return NULL;
}
return hl;
}
/**
* @function ends_listener
* @brief Termina il listener thread
*
* @param hl gestore del listener thread
*
* @note il thread listener capisce di dover terminare la propria esecuzione quando
* legge l'operazione TERMINATE nella pipe a cui è collegato
*/
void ends_listener(handler_listener_t *hl){
//controllo gli argomenti
if (hl == NULL) {
errno = EINVAL;
perror("ends_listener");
return;
}
long status = 0;
int check = 0;
//scrivo sulla pipe l'operazione di terminazione
check = write_pipe(hl->arg.pipe_fd, -1, TERMINATE);
if (check != 0) {
//terminazione un po' più brutale se la write non ha funzionato
fprintf(stderr,"Errore scrittura sulla pipe listener");
pthread_cancel(hl->tid);
}
//aspetto la terminazione del thread listener
check = pthread_join(hl->tid,(void*) &status);
#if defined(PRINT_STATUS)
fprintf(stdout, "status LISTENER = %ld\n", status);
#endif
//errore nel listener thread (chiama pthread_exit(errno) se ha avuto qualche errore)
if(status != 0) {
errno = status;
perror("Errore thread listener");
}
//errore nella join
if(check != 0) fprintf(stderr,"Errore nella join del listener");
//libero la memoria allocata per hl
free(hl);
}