Skip to content

Commit 15c71de

Browse files
committed
added signal handlers
- added signal handler to reap off zombie processes - added basic exception handling for smooth exit - closed unused listening sockets - added big test files to gitignore
1 parent 03140c0 commit 15c71de

18 files changed

+433
-197
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ obj/
1717
*.exe
1818
*.o
1919
*.out
20-
*.gch
20+
*.gch
21+
*.html
22+
*.mp4

makefile

+5-11
Original file line numberDiff line numberDiff line change
@@ -84,24 +84,18 @@ $(UTILS_OBJ_DIR)/%.o: $(UTILS_SRC_DIR)/%.cpp
8484
$(CXX) $(CFLAGS) -c -o $@ $<
8585

8686

87-
# ===================== CLEAN UP RULES =====================
87+
# ================ CLEAN UP RULES ==================
8888

8989
.PHONY: clean
9090

91-
clean: clean-server clean-client clean-utils clean-bin
91+
clean: clean-obj clean-bin
9292
@echo "Cleanup successful."
9393

94-
clean-server:
95-
$(RM) $(SERVER_OBJ_FILES)
96-
97-
clean-client:
98-
$(RM) $(CLIENT_OBJ_FILES)
99-
100-
clean-utils:
101-
$(RM) $(UTILS_OBJ_FILES)
94+
clean-obj:
95+
$(RM) $(shell find ./obj -name "*.o")
10296

10397
clean-bin:
104-
$(RM) ./bin/server ./bin/client
98+
$(RM) $(shell find ./bin -name "*.o")
10599

106100
# =========================================================
107101

src/client/createDataConnection.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,8 @@ int Client::createDataConnection(int controlConnectionfd) {
5454
dataConnectionfd = Accept(dataConnectionSocketfd, ipAddressOfDataServer);
5555
}
5656

57+
// listening is no longer needed
58+
close(dataConnectionSocketfd);
59+
5760
return dataConnectionfd;
5861
}

src/client/createSocketAndConnectToHost.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ int createSocketAndConnectToHost(const char* host, int portNumber) {
120120
// Print out IP address
121121
char s[INET6_ADDRSTRLEN];
122122
get_ip_str((const struct sockaddr*)p->ai_addr, s, sizeof s);
123-
fprintf(stdout, "\n[CLIENT:CONNECTION:test] Connected to %s\n", s);
123+
fprintf(stdout, "\n[CLIENT:CONNECTION] Connected to %s\n", s);
124124

125125
/* Don't need the structure with address info any more
126126
*

src/client/main.cpp

+21-35
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,31 @@
11
#include "./client.hpp"
22

3-
// prototypes for functions used below
4-
53
// parsing user's command line arguments
64
Client parseArgs(int, char**);
75

8-
// signal handlers
9-
void segmentationFaultHandler(int sig);
6+
// install Required signal handlers
7+
void InstallSignalHandlers();
108

119
// execution of client executable starts from here
1210
int main(int argc, char** argv) {
13-
// segmentation fault handler
14-
signal(SIGSEGV, segmentationFaultHandler);
15-
16-
// create a FTP client
17-
Client ftpClient = parseArgs(argc, argv);
18-
19-
// create a socket and connect to Server
20-
int controlConnectionfd = createSocketAndConnectToHost(
21-
ftpClient.getControlConnectionIP(), ftpClient.getControlConnectionPortNumber());
22-
23-
// Start the FTP protocol
24-
ftpClient.initiateProtocolInterpreter(controlConnectionfd);
11+
try {
12+
// installing signal Handlers
13+
// Here - segmentation fault
14+
InstallSignalHandlers();
15+
16+
// create a FTP client
17+
Client ftpClient = parseArgs(argc, argv);
18+
19+
// create a socket and connect to Server
20+
int controlConnectionfd =
21+
createSocketAndConnectToHost(ftpClient.getControlConnectionIP(),
22+
ftpClient.getControlConnectionPortNumber());
23+
24+
// Start the FTP protocol
25+
ftpClient.initiateProtocolInterpreter(controlConnectionfd);
26+
} catch (const std::exception& e) {
27+
// catch anything thrown within try block that derives from std::exception
28+
std::cerr << "\n[THROWN] : " << e.what() << "\n" << endl;
29+
}
2530
return 0;
26-
}
27-
28-
/**Segmentation Fault Handler
29-
*
30-
* Whenever the SIGSEGV signal is raised,
31-
* it is redirected here.
32-
*
33-
*/
34-
void segmentationFaultHandler(int sig) {
35-
void* array[10];
36-
size_t size;
37-
38-
// get void*'s for all entries on the stack
39-
size = backtrace(array, 10);
40-
41-
// print out all the frames to stderr
42-
fprintf(stderr, "Error: signal %d:\n", sig);
43-
backtrace_symbols_fd(array, size, STDERR_FILENO);
44-
exit(1);
4531
}

src/client/print.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
* print the string version of `errno` along with user's custom message
66
*/
77
void printError(const char* msg) {
8-
int saveErrNo = errno;
9-
fprintf(stderr, "\n[ERROR] : %s \n[ERROR code %d ] : %s\n ", msg, saveErrNo,
10-
strerror(saveErrNo));
8+
int saved_errno = errno;
9+
fprintf(stderr, "\n[ERROR] : %s \n[ERROR code %d ] : %s\n", msg, saved_errno,
10+
strerror(saved_errno));
1111

12-
if (saveErrNo == 32) {
12+
if (saved_errno == 32) {
1313
printf("\nServer end closed. \nExiting ...\n");
1414
exit(0);
1515
}

src/client/signalHandlers.cpp

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#include "./client.hpp"
2+
3+
/**Points to know when handling signals -
4+
*
5+
* 1.
6+
* Prefer `sigaction` over `signal`
7+
* link -
8+
* https://stackoverflow.com/a/232711
9+
*
10+
* 2.
11+
* When you install a custom signal handler, it overrides the default handler.
12+
* So be extremely careful about what you are handling,
13+
* and do you need to perform the actions (eg - freeing allocated memory )
14+
* what the default handler would have performed.
15+
* There are ways to call default signal handler after our custom handler,
16+
* but most of them are not pleasant.
17+
* The general idea of to reset the handler and raise the exception again.
18+
* Only do this when you know what you are doing.
19+
* link -
20+
* https://stackoverflow.com/questions/6015498/executing-default-signal-handler
21+
*
22+
* 3.
23+
* printf and other printing statements must be avoiding inside the handlers.
24+
* A workaround is to use a global volatile flag and set it inside the handler,
25+
* then in the main function, check if flag is marked, then use print statements there.
26+
* links -
27+
* https://stackoverflow.com/a/16891799
28+
* https://stackoverflow.com/a/16891065
29+
* https://stackoverflow.com/a/21712525
30+
*
31+
*/
32+
33+
// general functions
34+
void InstallSignalHandlers();
35+
void installSignalHandler(int signalType, void (*singalHandlerFunction)(int));
36+
37+
// signal handler functions
38+
void segmentationFaultHandler(int sig);
39+
void zombieProcessesHandler(int sig);
40+
41+
/**Wrapper function : Install Signal Handlers
42+
*
43+
* @usage
44+
* Cleanly install required signal handlers
45+
*
46+
*/
47+
void InstallSignalHandlers() {
48+
/**Handling segmentation fault
49+
*
50+
* In most cases, you should avoid handling this.
51+
* let the core get dumped, and try debugging from the dump.
52+
* But in some cases, we need more control, for debugging etc
53+
* this is useful in those cases.
54+
*/
55+
// installSignalHandler(SIGSEGV, segmentationFaultHandler);
56+
57+
// reap off zombie processes
58+
installSignalHandler(SIGCHLD, zombieProcessesHandler);
59+
}
60+
61+
62+
/**install signal handler
63+
*
64+
* @usage
65+
* install a single signal handler cleanly
66+
*
67+
* Note that,
68+
* sigaction is used instead of signal because,
69+
* sigaction is portable, predictable and thread safe.
70+
*
71+
*/
72+
void installSignalHandler(int signalType, void (*singalHandlerFunction)(int)) {
73+
struct sigaction sa;
74+
sa.sa_handler = singalHandlerFunction;
75+
sigemptyset(&sa.sa_mask);
76+
sa.sa_flags = SA_RESTART;
77+
if (sigaction(signalType, &sa, NULL) == -1) {
78+
printError();
79+
exit(1);
80+
}
81+
}
82+
83+
/**Segmentation Fault Handler
84+
*
85+
* Whenever the SIGSEGV signal is raised,
86+
* it is redirected here.
87+
*
88+
*/
89+
void segmentationFaultHandler(int sig[[gnu::unused]]) {
90+
// @logging
91+
// logs("A Segmentation fault occured.");
92+
// fprintf(stderr, "Error: signal %d:\n", sig);
93+
94+
// @logging : Print stack trace
95+
96+
void* array[10];
97+
size_t size;
98+
99+
// get void*'s for all entries on the stack
100+
size = backtrace(array, 10);
101+
102+
// print out all the frames to stderr
103+
backtrace_symbols_fd(array, size, STDERR_FILENO);
104+
105+
exit(1);
106+
}
107+
108+
109+
/**Reaper for zombie processes
110+
* When the parent doesn't collect the child's status, it becomes a zombie.
111+
*
112+
* @usage
113+
* The server main process (the one which always listens),
114+
* does not collect the status of the forked processes created
115+
* to handle the incoming clients.
116+
*
117+
* When the server runs indefinitely, a lot of clients come and go.
118+
* A lot of processes are created for these clients.
119+
* Therefore, a lot of zombies will be lying around if we don't clean them
120+
*
121+
* @exact-use-case on client side
122+
* 1. when the named pipe ( a child process ) in executeShellCommand is done.
123+
* 2. the job of data connection connection manager process is done.
124+
*
125+
*/
126+
void zombieProcessesHandler(int sig[[gnu::unused]]) {
127+
// @logging
128+
// logs("REAPING OFF ZOMBIE PROCESS");
129+
// fprintf(stderr, "Error: signal %d:\n", sig);
130+
131+
/**
132+
* The usual function of waitpid is to wait for child process to exit.
133+
*
134+
* However, WNOHANG in waitpid
135+
* tells it to return immediately if no child processes have exited yet.
136+
* It does not go into blocking state.
137+
*
138+
* waitpid() might overwrite errno, so we save and restore it
139+
*/
140+
int saved_errno = errno;
141+
while (waitpid(-1, NULL, WNOHANG) > 0)
142+
;
143+
errno = saved_errno;
144+
}

src/server/askForHelpAndCreateDataConnection.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ int Server::askForHelpAndCreateDataConnection(int controlConnectionfd) {
2626
string ipAddressOfClient;
2727
int dataConnectionfd;
2828
dataConnectionfd = Accept(helperSocket, ipAddressOfClient);
29-
29+
30+
// listening is no longer needed
31+
close(helperSocket);
32+
3033
return dataConnectionfd;
3134
}

src/server/commands/cmd_LIST.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ void Server::cmd_LIST(int controlConnectionfd, const vector<string>& tokens) {
3535
close(controlConnectionfd);
3636

3737
// @logging
38-
logs(getDataConnectionIP());
39-
logv(getDataConnectionPortNumber());
38+
// logs(getDataConnectionIP());
39+
// logv(getDataConnectionPortNumber());
4040

4141
Send(dataConnectionfd, "Directory Listing is as follows :");
4242
string commandToExecute = "ls -lA ";

src/server/commands/cmd_RETR.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ void Server::cmd_RETR(int controlConnectionfd, const vector<string>& args) {
4141
close(controlConnectionfd);
4242

4343
// @logging
44-
logs(getDataConnectionIP());
45-
logv(getDataConnectionPortNumber());
44+
// logs(getDataConnectionIP());
45+
// logv(getDataConnectionPortNumber());
4646

4747
Send(dataConnectionfd, "Sending File in Binary Mode.");
4848

src/server/commands/cmd_STOR.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ void Server::cmd_STOR(int controlConnectionfd, const vector<string>& args) {
4040
// Child no longer needs control connection, we can close it
4141
close(controlConnectionfd);
4242

43-
4443
// @logging
45-
logs(getDataConnectionIP());
46-
logv(getDataConnectionPortNumber());
44+
// logs(getDataConnectionIP());
45+
// logv(getDataConnectionPortNumber());
4746

4847
Recv(dataConnectionfd, ftpResponse);
4948
logs(ftpResponse.c_str());

src/server/createDataConnection.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
*
1717
*/
1818
int Server::createDataConnection(int controlConnectionfd) {
19-
logs("[SERVER] : I am creating a new data Connection");
20-
19+
printInfo("[SERVER] : I am creating a new data Connection");
2120

2221
const char* ip = getDataConnectionIP();
2322
int port = getDataConnectionPortNumber();
@@ -26,9 +25,7 @@ int Server::createDataConnection(int controlConnectionfd) {
2625
bool doesRouteExist = doesRouteToHostExist(ip, port);
2726

2827
// @logging
29-
// logServerConfiguration();
30-
logs(ip);
31-
logv(port);
28+
logServerConfiguration();
3229

3330
/**Special Note : Race Condition Resolving
3431
*

src/server/createSocketAndConnectToHost.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ int createSocketAndConnectToHost(const char* host, int portNumber) {
125125
// Print out IP address
126126
char s[INET6_ADDRSTRLEN];
127127
get_ip_str((const struct sockaddr*)p->ai_addr, s, sizeof s);
128-
fprintf(stdout, "\n[SERVER:CONNECTION] Connected to %s\n", s);
128+
fprintf(stdout, "\n[SERVER-INITIATED:CONNECTION] Connected to %s\n", s);
129129

130130
/* Don't need the structure with address info any more
131131
*

src/server/handleZombies.cpp

-21
This file was deleted.

0 commit comments

Comments
 (0)