-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathATM.c
388 lines (340 loc) · 14.5 KB
/
ATM.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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/**
* Term Project - Concurrency, IPC, Semaphores, Shared Memory
*
* ATM
*
* Carleton University
* Department of System and Computer Engineering
* SYSC 4001 - Operating Systems
*
* @authors Evan Smedley, 101148695 & Trong Nguyen, 100848232
* @version v1.00
* @release April 27, 2021
*
* The ATM component manages the interaction between the
* customer and the database (DB) via an account number and a
* pin. The ATM allows the customers with various interactions
* with the DB such as depositing and withdrawing a balance.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include "structs.h"
#include "message_queue.h"
#include "semaphore.h"
#include "shared_memory.h"
/*
* Check if user input is a valid int.
*/
bool all_int_char(char *user_input) {
for (int i = 0; user_input[i] != '\n'; i++) {
if ((user_input[i] < '0') || (user_input[i] > '9')) {
return false;
}
}
return true;
}
/*
* Check if user input is a valid float.
*/
bool all_float_char(char *user_input) {
for (int i = 0; user_input[i] != '\n'; i++) {
if ((user_input[i] < '0') || (user_input[i] > '9')) {
if (user_input[i] != '.'){
return false;
}
}
}
return true;
}
/*
* Find the index of the newline character.
*/
int locate_newline(char *user_input) {
int i = 0;
while (user_input[i] != '\n') {
i++;
}
return i;
}
/*
* Flush the stidn buffer.
*/
void empty_stdin_buffer() {
int c = getchar();
while(c != '\n' && c != EOF) {
c = getchar();
}
}
/**
* Accesses a message queue with the key 1224. Walks the user through accessing account information.
* Requires the user to input their account number and pin, if the user gets the pin wrong 3 times,
* blocks the account. Takes user input to determine if the user would like to view their balance,
* do a withdrawal or deposit.
*/
void atm() {
// Create primary message queue
int msgqid = msgq_create((key_t) 1224);
// Get access to message queue for writing program state to a file
int state_msgqid = msgq_create((key_t) 8888);
// Get access to semaphore used for counting number of active ATM's
int semid1 = sem_create((key_t) 7564);
// Increment the semaphore because this is a new ATM
sem_release(semid1);
// Get access to semaphore that gives an ATM id to this ATM
int semid2 = sem_create((key_t) 7565);
int atm_id = sem_getval(semid2);
sem_release(semid2);
// Get access to shared memory that stores an array of semaphore keys for protecting accounts
int shmid_account_sem = shm_create((key_t) 4567, sizeof(locks_t));
locks_t *account_locks = (locks_t *) shm_access(shmid_account_sem);
// Initialize variables
msg_t send_buffer;
msg_t receive_buffer;
bool valid_acc = false;
bool valid_pin = false;
bool loop = true;
bool get_account_no = true;
int pin_attempts;
char temp_float[20];
char operation_input[3];
int message_size = sizeof(send_buffer.account_no) + sizeof(send_buffer.pin) + sizeof(send_buffer.amount);
state_msg_t state_buffer;
state_buffer.msg_type = 1;
int temp_semid;
char temp_int[7];
bool valid_input;
int newline_location;
bool cont;
// Print state
sprintf(state_buffer.message, "ATM %d -> Online\nNumber of atms online: %d", atm_id, sem_getval(semid1));
msgq_send(state_msgqid, (void *)&state_buffer, sizeof(state_buffer.message));
// Loop until the user requests to exit
while (loop) {
// Take user input for the account number
valid_input = true;
valid_pin = false;
printf("\n-------------------------------------------------\nATM %d\n-------------------------------------------------", atm_id);
printf("\nPlease type your 5-digit account number >>> ");
fgets(send_buffer.account_no, sizeof(send_buffer.account_no), stdin);
if (send_buffer.account_no[0] == 'X') {
printf("Thank you for using this ATM!\n");
break;
}
newline_location = locate_newline(send_buffer.account_no);
if (newline_location > 5) {
empty_stdin_buffer();
}
// Make sure that the user input is the right length
if (newline_location != 5) {
valid_input = false;
send_buffer.account_no[0] = '9';
}
// Make sure that all characters in the inputs are numbers
if (!all_int_char(send_buffer.account_no)) {
valid_input = false;
send_buffer.account_no[0] = '9';
}
if (valid_input) {
send_buffer.account_no[5] = '\0';
// Set the send buffer message type to 1 to represent a PIN message
send_buffer.msg_type = 1;
pin_attempts = 0;
// This must be set to a positive float because later on, if amount is negative, it is an indicator
send_buffer.amount = 1.0;
// Allow the user 3 tries to get the pin right
while (pin_attempts < 3) {
// Take user input for the pin
printf("\nPlease type your 3-digit pin >>> ");
fgets(send_buffer.pin, sizeof(send_buffer.pin), stdin);
newline_location = locate_newline(send_buffer.pin);
if (newline_location > 3) {
empty_stdin_buffer();
}
// Make sure that the user input is the right length
if (newline_location != 3) {
printf("hi");
valid_input = false;
}
// Make sure that all characters in the inputs are numbers
if (!all_int_char(send_buffer.pin)) {
valid_input = false;
printf("hi1");
}
send_buffer.pin[3] = '\0';
if (send_buffer.pin[0] == 'X') {
valid_pin = false;
printf("hi2");
break;
}
// Send PIN message
msgq_send(msgqid, (void *)&send_buffer, message_size);
// Wait to receive PIN_WRONG or OK message
msgq_receive(msgqid, (void *)&receive_buffer, message_size, 6);
// Interpret message
// Pin is correct
if (receive_buffer.pin[0] == 'y') {
printf("\nPin correct!\n");
valid_pin = true;
for (int i = 1; i < NUM_LOCKS * 2; i += 2) {
strncpy(temp_int, send_buffer.account_no, 5);
temp_int[5] = '\0';
if (account_locks->locks[i] == atoi(temp_int)) {
temp_semid = sem_create((key_t) account_locks->locks[i - 1]);
if (sem_getval(temp_semid) == 0) {
printf("\nAnother user is using this account, you will have to wait\n");
}
sem_acquire(temp_semid);
break;
}
}
break;
// Pin is incorrect
} else if (receive_buffer.pin[0] == 'n') {
printf("\nPin incorrect.\n");
pin_attempts += 1;
// There was a database server error
} else {
printf("\nInvalid database server response");
exit(EXIT_FAILURE);
}
}
}
// Valid_acc will be true if the user got the account number and pin correct
if (valid_pin && valid_input) {
// Print state
sprintf(state_buffer.message, "ATM %d: signed in to account b", atm_id);
msgq_send(state_msgqid, (void *)&state_buffer, sizeof(state_buffer.message));
// Loop forever
while (true) {
// Take user input for the desired operation
printf("\nWould you like to:\n[0] VIEW BALANCE\n[1] WITHDRAW\n[2] DEPOSIT\n[X] EXIT\n>>> ");
fgets(operation_input, sizeof(operation_input), stdin);
newline_location = locate_newline(operation_input);
if (newline_location > 1) {
empty_stdin_buffer();
operation_input[0] = '9';
} else if (newline_location != 1) {
operation_input[0] = '9';
}
if (operation_input[0] == '0' || operation_input[0] == '1' || operation_input[0] == '2') {
cont = true;
if (operation_input[0] == '0') {
// Set up the send buffer with the BALANCE message
send_buffer.msg_type = 2;
} else if (operation_input[0] == '1') {
// Set up the send buffer with the WITHDRAW message
send_buffer.msg_type = 3;
printf("\nHow much would you like to withdraw? >>> ");
fgets(temp_float, sizeof(temp_float), stdin);
if (!all_float_char(temp_float)) {
cont = false;
} else {
send_buffer.amount = atof(temp_float);
if (send_buffer.amount < 0) {
cont = false;
printf("\nYou can't withdraw a negative amount\n");
}
}
} else {
// Set up the send buffer with a DEPOSIT message
send_buffer.msg_type = 3;
printf("\nHow much would you like to deposit? >>> ");
fgets(temp_float, sizeof(temp_float), stdin);
if (!all_float_char(temp_float)) {
cont = false;
} else {
send_buffer.amount = atof(temp_float) * -1.0;
if (send_buffer.amount > 0) {
cont = false;
printf("\nYou can't deposit a negative amount\n");
}
}
}
if (cont) {
// Send the message (either BALANCE, WITHDRAW or DEPOSIT)
msgq_send(msgqid, (void *)&send_buffer, message_size);
// Wait for response
msgq_receive(msgqid, (void *)&receive_buffer, message_size, 7);
// Interpret response
if (operation_input[0] == '0') {
// Display balance
printf("\nYour balance is: %.2f\n", receive_buffer.amount);
} else {
// Display withdraw and deposit information
if (receive_buffer.account_no[0] == 'y') {
printf("\nSuccess!\nNew balance: %.2f\n", receive_buffer.amount);
} else if (receive_buffer.account_no[0] == 'n') {
if (send_buffer.amount < 0 && operation_input[0] == '1') {
printf("\nWithdrawals must be positive\n");
} else if (send_buffer.amount > 0 && operation_input[0] == 2) {
printf("\nDeposits must be positive\n");
} else {
printf("\nNot enough funds available to process this withdrawal\n");
}
} else {
printf("\nInvalid database server response\n");
exit(EXIT_FAILURE);
}
}
}
// This if is true if the user wants to exit
} else if (operation_input[0] == 'X') {
printf("\nThank you for using this ATM! Have a nice day!\n");
break;
// If the user choice is invalid they must try again
} else {
printf("\nInvalid entry, please try again.\n");
}
}
sem_release(temp_semid);
// Print state
sprintf(state_buffer.message, "ATM %d: signed out of account b", atm_id);
msgq_send(state_msgqid, (void *)&state_buffer, sizeof(state_buffer.message));
} else if (pin_attempts == 3) {
// This means that the PIN was incorrect 3 times so the account must be blocked
// Set the send buffer amount to a negative float, this is how the server knows to block the account
send_buffer.amount = -1.0;
msgq_send(msgqid, (void *)&send_buffer, message_size);
printf("\nAccount blocked.\nToo many incorrect attempts at inputting the pin\nPlease try another account.\n\n");
// Print state
sprintf(state_buffer.message, "ATM %d: account b is blocked", atm_id);
msgq_send(state_msgqid, (void *)&state_buffer, sizeof(state_buffer.message));
} else if (!valid_input) {
printf("\nThat was not a valid input, please try again\n");
} else {
printf("\nThat was not a valid pin, please try again\n");
}
}
// Decrement number of active ATM's because this one is closing
sem_acquire(semid1);
// Print state
sprintf(state_buffer.message, "ATM %d -> Offline\nNumber of atms online: %d", atm_id, sem_getval(semid1));
msgq_send(state_msgqid, (void *)&state_buffer, sizeof(state_buffer.message));
}
void parent(pid_t child_pid) {
// Allows time for the DB_editor to initialize and acquire the semaphore
sleep(1);
// Get access to the semaphore, finish executing when the DB_editor releases the semaphore
int exit_atms_semid = sem_create((key_t) 9090);
sem_acquire(exit_atms_semid);
sem_release(exit_atms_semid);
kill(child_pid, SIGTERM);
printf("\nATM Offline\n");
}
int main() {
pid_t pid = fork();
if (pid == 0) {
atm(pid);
} else if (pid > 0) {
parent(pid);
} else if (pid < 0) {
fprintf(stderr, "Database server fork failed\n");
exit(EXIT_FAILURE);
}
}