-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
303 lines (260 loc) · 10.4 KB
/
main.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
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <util.h>
#include <icl_hash.h>
#include <estimate_t.h>
#include <fcntl.h>
int checkargs(int argc, char* argv[]);
void* scompatta(long long int*, int*, char*);
void printTable(int fd, icl_hash_t* ht);
void sigIntHandler();
void sigAlarmHandler();
//Variabili globali
icl_hash_t *htable=NULL; //Tabella hash
volatile sig_atomic_t terminate=0; //Condizione di uscita dal ciclo Select
int *pid=NULL; //Array di PID dei servers attivati
int main(int argc, char* argv[]){
int k=0; //Numero di servers totali
int retread=0;
char tmp[MAXTOWRITE]; //buffer temporaneo in cui salvo la stima letta dalla pipe
fd_set set; FD_ZERO(&set); //Insieme di file descriptor attivi
fd_set rdset; FD_ZERO(&rdset);//Copia di set da passare alla select
int fd_max=-1; //Numero di fd attivi
struct sigaction sInt, sAlarm;
memset(&sInt, 0, sizeof(sInt)); //Mi accerto che s sia stato inizializzato a 0
memset(&sAlarm, 0, sizeof(sAlarm));
sInt.sa_handler=sigIntHandler; //Registro gestore SIGINT
sAlarm.sa_handler=sigAlarmHandler;//Registro gestore SIGALRM
//maschera segnali fino all'installazione del gestore
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGALRM);
//Controllo argomenti
k=checkargs(argc,argv);
printf("SUPERVISOR STARTING %d\n",k);
//Alloco l'hash table
if ((htable=icl_hash_create(NBUCKETS,NULL,compareInt))==NULL){
fprintf(stderr, "Allocating hash table");
exit(EXIT_FAILURE);
}
//Array di pipe: alla posizione i trovo la pipe con il server i.
//pipes[i][1] può essere usato per scrivere sulla pipe
//pipes[i][0] può essere usato per leggere dalla pipe
int **pipes=NULL;
ALLOCA(pipes, sizeof(int*)*k)
//Array contenente i pid dei servers
pid=malloc(sizeof(int)*k);
//Installo gestore segnali
SYS(sigaction(SIGINT, &sInt, NULL), -1, "Signal handler doesn't works. Exiting...")
SYS(sigaction(SIGALRM,&sAlarm, NULL),-1,"Siglan handler doesn't works. Exiting...")
sigemptyset(&sigset);
//Avvia K servers distinti, aprendo una pipe con ogni server.
char* s1 = NULL; ALLOCA(s1, sizeof(int))
char* s2 = NULL; ALLOCA(s2, sizeof(int))
for (int i = 0; i < k; i++) {
ALLOCA(pipes[i], sizeof(int)*2)
SYS((pipe(pipes[i])),-1,"opening pipes")
//Se una fork dovesse fallire, terminiamo bruscamente
if ((pid[i]=fork())==-1){
perror("in fork: ");
exit(EXIT_FAILURE);
}
else if (pid[i]==0) { //figlio: esegue codice server
sprintf(s1,"%d",i);
sprintf(s2,"%d", pipes[i][1]);
execl("server", "server", s1, s2, NULL);
perror("exec returned");
exit(EXIT_FAILURE);
}
close(pipes[i][1]);
FD_SET(pipes[i][0],&set); //Registra i fd delle pipe nella maschera per la Select
if (pipes[i][0] > fd_max) fd_max = pipes[i][0];
//Settiamo il FD per non bloccarsi alla read
fcntl(pipes[i][0], F_SETFL,O_NONBLOCK);
}
rdset=set;
free(s1);
free(s2);
//Attende per una pipe pronta. Esce dal while solo con un doppio SIGINT
while (!terminate){
struct timeval timeout={0,150}; //Timer per la select
//Non effettuiamo il controllo sul valore di ritorno della select
//Per non disturbare il gestore di segnali installato.
errno=0;
while ((select(fd_max+1,&rdset,NULL,NULL,&timeout)==-1) && errno==EINTR);
rdset=set; //Necessario! la select modifica per indicare chi è pronto
//Scandisce i FD per gestire quello pronto
for (int i = 0; i < k; i++) {
if (FD_ISSET(pipes[i][0],&rdset)) {
//Si rimette in lettura se viene interrotto da un segnale
errno=0;
while (((retread=read(pipes[i][0], tmp, MAXTOWRITE))==-1) && (errno==EINTR));
//Gestisce errori read
//Nota: EAGAIN vuol dire che la read non è ancora pronta
//Avendo impostato il FD come O_NON_BLOCK è necessario il controllo
if (retread==-1) {
if (errno == EAGAIN) continue;
else {perror("failure on read"); exit(errno);}
}
//EOF arriva quando il server ha chiuso il descrittore
// Questo non deve avvenire!
SYS(retread,0,"Un server ha chiuso la connessione!")
//Per come funziona il server, manderà: "ID,STIMASECRET"
//Splitta in due variabili separate
long long int *id=NULL; ALLOCA(id, sizeof(long long int))
int *stima=NULL; ALLOCA(stima, sizeof(int))
if (scompatta(id,stima,tmp)==NULL)
fprintf(stderr, "Message from server unreadable: %s\n", tmp);
printf("SUPERVISOR ESTIMATE %d FOR %x FROM %d\n", \
*stima, (int)*id, i);
fflush(stdout);
//Controlla se esistono già stime per il client ID
//Se non trova chiave -> inserisce nell'hash
estimate_t *t = NULL;
if ((t=icl_hash_find(htable, (void*) id))==NULL) {
estimate_t* e=NULL; ALLOCA_ELEMENTO(e)
*(e->id) =*id; free(id);
*(e->estimate)=*stima; free(stima);
*(e->nservers)=1;
if ((icl_hash_insert(htable, (void *)(e->id), (void *)e)) == NULL)
fprintf(stderr, "Inserimento: una stima di %lld non è stata inserita.\n" \
"La stima potrebbe non essere corretta. \n", *(e->id));
}
//Altrimenti, elimina vecchi dati ID e
// inserisci una stima tra i dati vecchi e quelli nuovi
else{
estimate_t* e=NULL; ALLOCA_ELEMENTO(e)
*(e->id)=*id; free(id);
*(e->estimate)=approximate(*stima, *(t->estimate));free(stima);
*(e->nservers)=*(t->nservers)+1;
if (icl_hash_delete(htable,(void*)(e->id),free,freeElement)==-1)
fprintf(stderr, "Aggiornamento: una stima di %lld non è stata inserita.\n" \
"La stima potrebbe non essere corretta. \n", *(e->id));
if (icl_hash_insert(htable,(void*)(e->id),(void*)e) == NULL)
fprintf(stderr, "Aggiornamento: una stima di %lld non è stata inserita.\n" \
"La stima potrebbe non essere corretta. \n", *(e->id));
}//End inserimento
memset(tmp,'\0', sizeof(tmp));
} //End-FD_ISSET
} //END-FOR
}
//SIGALRM arrivato: stampa tabella
printTable(1,htable);
printf("SUPERVISOR EXITING\n");
//Chiudi pipes e libera memoria.
icl_hash_destroy(htable, free, freeElement);
for (int j = 0; j < k; j++) {
kill(pid[j],SIGPIPE);
close(pipes[j][0]);
free(pipes[j]);
}
free(pid);
if (pipes)
free(pipes);
return 0;
}
/**
* Effettua controllo iniziale sugli argomenti del main
* @param argc numero di elementi in argv[]
* @param argv argomenti del programma
* @pre : argc>0,
* argv[]!=NULL && for each s in argv.(s!=NULL)
* @return k: #server da creare
*/
int checkargs(int argc, char* argv[])
{
int k=0;
//Controlli sulle requires
if (argc<=0 || argv==NULL){
fprintf(stderr, "Requires violate in checkargs\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < argc; i++) {
if (argv[i]==NULL){
fprintf(stderr, "Requires violate in checkargs\n");
exit(EXIT_FAILURE);
}
}
//Se non viene passato k, si richiede in input.
if (argc!=2) {
char* tmp = NULL;
ALLOCA(tmp, (sizeof(int)))
fgets(tmp, sizeof(int),stdin);
if ((k=strtol(tmp,NULL,0))<=0){
fprintf(stderr,"%s: argomento non valido.\n", tmp);
free(tmp);
exit(EXIT_FAILURE);
}
free(tmp);
}
//Se l'argomento passato non è un intero, termina.
else {
if ((k = strtol(argv[1], NULL, 0)) <= 0) {
fprintf(stderr, "%s: argomento non valido.\n", argv[1]);
exit(EXIT_FAILURE);
}
}
return k;
}
/**
* Separa i campi l ed i da s
* @param s: stringa del tipo l,i
* @param i: puntatore in cui salvare la seconda parte di s
* @param l: puntatore in cui salvare la prima parte di s
* @return : 1 (success), NULL altrimenti
*/
void* scompatta(long long int* l, int* i, char* s)
{
char* tok=NULL;
tok=strtok(s,",");
if(tok==NULL) return NULL;
*l=(long long int)strtol(tok,NULL,0);
tok=strtok(NULL,",");
if (tok==NULL) return NULL;
*i=strtol(tok,NULL,0);
if ((*l)<0 || (*i)<0)
return NULL;
return (void*)1;
}
/**
* Print <key, value> for each couple in the hash table.
* @param fd : where to print
* @param ht : htable to print
* @pre : fd>=0 &&
*/
void printTable(int fd, icl_hash_t* ht)
{
int iterator=0; icl_entry_t *pointer=NULL; long long int* key=0; estimate_t* data=0;
icl_hash_foreach(ht,iterator,pointer,key,data)
{
if ((dprintf(fd,"SUPERVISOR ESTIMATE %d FOR %x BASED ON %d\n", \
(int) *(data->estimate), (int)*(long long int*)key, (int) *(data->nservers))) < 0 ){
fprintf(stderr, "File in cui stampare non trovato. \n");
exit(EXIT_FAILURE);
}
fflush(stdout);
}
}
/**Gestisce l'interruzione settando un allarme ad un secondo.
*Se è la seconda interruzione in un secondo
* -> Esci dal loop, stampando la tabella nello stdout e deallocando la memoria.
*/
void sigIntHandler()
{
int alarmsnumber;
//Se non ci sono allarmi già settati, avvia il timer ed esci dalla funzione.
if ((alarmsnumber=alarm(1))==0) return;
else terminate=1;
}
/**Gestisce l'allarme avviato dal SIGINT: stampa la tabella sullo stderr */
void sigAlarmHandler()
{
printTable(2,htable);
}