-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchatty.c
179 lines (140 loc) · 5.34 KB
/
chatty.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
/*
* membox Progetto del corso di LSO 2017/2018
*
* Dipartimento di Informatica Università di Pisa
* Docenti: Prencipe, Torquati
*
*/
/**
* @file chatty.c
* @brief File principale del server chatterbox
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
/* inserire gli altri include che servono */
#include <stats.h>
#include <config.h>
#include <error_handler.h>
#include <fd_queue.h>
#include <pipe_fd.h>
#include <listener.h>
#include <signal_handler.h>
#include <thread_pool.h>
#include <files_handler.h>
/* -------------------------- strutture dati globali --------------------------- */
/* struttura che memorizza le statistiche del server, struct statistics
* e' definita in stats.h.
*/
struct statistics chattyStats = { 0,0,0,0,0,0,0 };
pthread_mutex_t mtx_stats = PTHREAD_MUTEX_INITIALIZER;
//configurazioni del server
configs_t conf_server = { NULL,0,0,0,0,0,NULL,NULL };
/* ---------------------- altre strutture dati utilizzate ----------------------- */
//coda degli fd printi ad inviare una richiesta al server (utilizzata sia dal listener che dai thread worker)
static fd_queue_t *fd_queue = NULL;
// pipe utilizzata dai worker thread per comunicare con il listener thread.
// E' utilizzata anche per far terminare l'esecuzione del thread listener
static pipe_fd_t *pipe_fd = NULL;
//gestore del listener thread
static handler_listener_t *hl_listener = NULL;
//gestore thread pool
static hl_thread_pool_t *thread_pool = NULL;
/* ---------------------------- funzioni di utilità ---------------------------- */
static void usage(const char *progname) {
fprintf(stderr, "Il server va lanciato con il seguente comando:\n");
fprintf(stderr, " %s -f conffile\n", progname);
}
/**
* @function clean_all
* @brief Libera la memoria occupata
*/
static void clean_all() {
//termino i threads
if(hl_listener != NULL) ends_listener(hl_listener);
if(thread_pool != NULL) ends_thread_pool(thread_pool);
//cancello tutti i file inviati dagli utenti
clean_dirfile(conf_server.dir_name);
//cancello le strutture dati
clean_configs(&conf_server);
if(fd_queue != NULL) clean_fd_queue(fd_queue);
if(pipe_fd != NULL) clean_pipe_fd(pipe_fd);
#if defined(PRINT_STATUS)
fprintf(stdout,"CLEAN ALL: chiamata e terminata\n");
#endif
}
int main(int argc, char *argv[]) {
if (argc!=3 || strncmp(argv[1],"-f",2)!=0) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
#if defined(PRINT_STATUS)
fprintf(stdout," PRINT_STATUS definita\n");
#endif
//variabile di controllo
int check;
/* ------------------------- creazione strutture dati -------------------------- */
//prendo le variabili di configurazione server dal file
check = configura_server(argv[2],&conf_server);
err_exit(check,-1,clean_all());
//creo la coda per gli fd
fd_queue = init_fd_queue();
err_exit(fd_queue,NULL,clean_all());
//creo la pipe per le comunicazioni extra tra workers e listener
pipe_fd = init_pipe_fd();
err_exit(pipe_fd,NULL,clean_all());
/* ----------------------------- creazione threads ------------------------------- */
//setto la maschera per i segnali da passare al signal_handler
//e che viene ereditata dagli altri threads
sigset_t set;
sigemptyset(&set); // resetto tutti i bits
sigaddset(&set, SIGUSR1); // aggiunto SIGUSR1 --> per stampare le statistiche
sigaddset(&set, SIGINT); // aggiunto SIGINT --> per terminare chatterbox
sigaddset(&set, SIGTERM); // aggiunto SIGTERM --> per terminare chatterbox
sigaddset(&set, SIGQUIT); // aggiunto SIGQUIT --> per terminare chatterbox
sigaddset(&set, SIGPIPE); // aggiunto SIGPIPE --> da ignorare
sigaddset(&set, SIGUSR2); // aggiunto SIGUSR2 --> per terminare chatterbox
//NOTA su SIGUSR2: inviato dagli altri threads al signal_handler in caso di errore
// blocco i segnali
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
fprintf(stderr, "ERROR: pthread_sigmask\n");
clean_all();
return (EXIT_FAILURE);
}
//avvio il signal_handler
pthread_t tid_sh;
args_sig_handler_t sh_args = { &set, clean_all };
if (pthread_create(&tid_sh, NULL, signal_handler, &sh_args) != 0) {
fprintf(stderr, "pthread_create failed (signal_handler)\n");
clean_all();
exit(EXIT_FAILURE);
}
//avvio il thradpool dei worker
thread_pool = starts_thread_pool(fd_queue, pipe_fd, tid_sh);
err_exit(thread_pool,NULL,clean_all());
//avvio il listener
hl_listener = starts_listener(fd_queue, pipe_fd, tid_sh);
err_exit(hl_listener,NULL,clean_all());
/* ------------------------------ terminazione chatterbox ------------------------------ */
//aspetto la terminazione del signal handler
long status = 0;
check = pthread_join(tid_sh, (void*) &status);
//errore nella join
if(check != 0) {
errno = check;
perror("Errore thread signal_handler");
clean_all();
exit(EXIT_FAILURE);
}
//se terminato per errore (ha già ripulito tutto il signal_handler anche in caso di errore)
else if(status != 0) exit(EXIT_FAILURE);
#if defined(PRINT_STATUS)
fprintf(stdout, "status SH = %ld\n", status);
#endif
return 0;
}