+#define _GNU_SOURCE
+
+/* Beaware: this program uses GNU extensions (the TEMP_FAILURE_RETRY macro)
+ * I am writing (non-)portable code because I am running out of time
+ * TODO: my portable macro with the same funcionality
+ */
+
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include "javafork.h"
-pid_t daemonPID; /*Stores the daemon server PID*/
-int sockfd = -1; /*Stores the daemon server TCP socket.*/
-
+pid_t daemonPID; /*Stores the daemon server PID*/
+int sockfd = -1; /*Stores the daemon server TCP socket.*/
-static int restartableClose(int fd)
-{
- return TEMP_FAILURE_RETRY(close(fd));
-}
{
/*If we always initialize file descriptor variables with value -1*/
/*this method should work like a charm*/
- return (fd == -1) ? 0 : restartableClose(fd);
+ return (fd == -1) ? 0 : TEMP_FAILURE_RETRY(close(fd));
}
char *avalue = IPADDRESS; /*Address: numeric value or hostname*/
int pvalue = PORT; /*TCP port*/
int qvalue = QUEUE; /*TCP listen queue*/
+ struct sigaction sa; /*sig actions values*/
/*This process is intended to be used as a daemon, it sould be launched by the INIT process, because of that*/
/*Changing session.*/
setsid();
-
- if (signal(SIGPIPE,SIG_IGN) == SIG_ERR) {
- syslog (LOG_ERR, "signal SIGPIPE desactivation failed: %m");
- return 1;
- }
+
opterr = 0;
while ((c = getopt (argc, argv, "a:p:q:")) != -1) {
return 1;
}
- /*INIT process sending SIGINT? Should I catch that signal?*/
- daemonPID = getpid();
- if (signal(SIGINT, sigint_handler) == SIG_ERR) {
- syslog (LOG_ERR, "SIGTERM signal handler failed: %m");
- return 1;
- }
+
+ /* From man sigaction(2):
+ * A child created via fork(2) inherits a copy of its parent's signal dispositions.
+ * During an execve(2), the dispositions of handled signals are reset to the default; the
+ * dispositions of ignored signals are left unchanged.
+ * I want to ignore SIGCHLD without causing any issue to child processes.
+ */
+ memset (&sa, 0, sizeof(sa));
+ /*SIG_DFL: by default SIGCHLD is ignored.*/
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ if (sigaction(SIGCHLD, &sa, NULL) < 0) {
+ syslog (LOG_ERR, "SIGCHLD signal handler failed: %m");
+ return 1;
+ }
+
+
+ /*INIT process sending SIGINT? Should I catch that signal?*/
+ daemonPID = getpid();
+ memset (&sa, 0, sizeof(sa));
+ sa.sa_handler = &sigint_handler;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGINT, &sa, NULL) < 0) {
+ syslog (LOG_ERR, "SIGINT signal handler failed: %m");
+ return 1;
+ }
+
if (main_daemon (avalue, pvalue, qvalue) < 0)
return 1;
goto err;
}
- while(1) {
+ for(;;) {
clilen = sizeof(addr_client);
if ((sockclient = TEMP_FAILURE_RETRY(accept (sockfd, (struct sockaddr *) &addr_client, &clilen))) < 0) {
syslog (LOG_ERR, "socket accept failed: %m");
void *serverThread (void * arg)
{
- int socket = -1; /*Open socket by the Java client*/
- long timeout, utimeout; /*Timeout for reading data from client: secs and usecs respectively*/
- int len; /*Control parameter used while receiving data from the client*/
- char buffer[1025]; /*This buffer is intended to store the data received from the client*/
- char *command = NULL; /*The command sent by the client, to be executed by this process*/
- uint32_t *commandLength = NULL; /*Store the command length*/
+ int socket = -1; /*Open socket by the Java client*/
+ long timeout, utimeout; /*Timeout for reading data from client: secs and usecs*/
+ /*respectively*/
+ int len; /*Control parameter used while receiving data from the client*/
+ char buffer[1025]; /*This buffer is intended to store the data received from the client*/
+ char *command = NULL; /*The command sent by the client, to be executed by this process*/
+ uint32_t *commandLength = NULL; /*Store the command length*/
socket = (int) arg;
return -1;
}
- /*TODO: keepalive is not enough to find out if the connection is broken */
- /* apparently it just works while the handshake phase. See: */
- /* http://stackoverflow.com/questions/4345415/socket-detect-connection-is-lost */
- /* I have to implement an echo/ping messages protocol (application layer) */
+ /*TODO: keepalive is not enough to find out if the connection is broken */
+ /* apparently it just works while the handshake phase. See: */
+ /* http://stackoverflow.com/questions/4345415/socket-detect-connection-is-lost */
+ /* I have to implement an echo/ping messages protocol (application layer) */
return 0;
}
syslog (LOG_INFO, "read TCP socket spurious readiness");
}
} else if (received == 0) {
- /*if nData is 0, client closed connection but we wanted to receive more data, this is an error */
+ /*if nData is 0, client closed connection but we wanted to receive more data, */
+ /*this is an error */
syslog (LOG_ERR, "expected more data, closed connection from client");
return -1;
}
}
+
int pre_fork_system(int socket, char *command)
{
/*Required variables in order to share memory between processes*/
/*Required variables in order to share the semaphore between processes*/
key_t keysemaphore;
int idsemaphore = -1;
- sem_t *semaphore = NULL; /*Used as a barrier: the child process just can start after sending the XML init code*/
-
- int returnValue = -1; /*Return value from this function can be caught by upper layers, NOK by default*/
+ sem_t *semaphore = NULL; /*Used as a barrier: the child process just can start after */
+ /*sending the XML init code*/
+ int returnValue = -1; /*Return value from this function can be caught by upper*/
+ /*layers, NOK by default*/
- /*Allocate shared memory because we can not use named semaphores*/
- /*We are using this semaphore as a barrier, because we just want to start the child process when the parent process has sent*/
- /*the XML header (see: fork_system function)*/
-
- keysemaphore=ftok("/bin/ls", SHAREMEMSEM); /*the /bin/ls must exist otherwise this does not work... */
+ /* Allocate shared memory because we can not use named semaphores
+ * We are using this semaphore as a barrier, because we just want to start the child process
+ * when the parent process has sent the XML header (see: fork_system function)
+ */
+
+ /*the /bin/ls must exist otherwise this does not work... */
+ keysemaphore=ftok("/bin/ls", SHAREMEMSEM);
if (keysemaphore == -1) {
syslog (LOG_ERR, "ftok failed: %m");
goto end;
- /*Allocate shared memory for the return status code from the process which is going to be launched by the system function.*/
- /*We want to share the returnstatus variable between this process and the child that is going to be created in the fork_system method.*/
- /*The goal is to store in this variable the return status code received from the process launched with the system method by the child process,*/
- /*then the parent process can retrieve that return status code and send it by TCP to the Java client.*/
- /*There are not concurrency issues because the parent process will just try to read this variable when the child process is dead, taking*/
- /*in that moment its last value and sending it to the Java client.*/
-
-
- keyvalue=ftok("/bin/ls", SHAREMEMKEY); /*the /bin/ls must exist otherwise this does not work... */
+ /* Allocate shared memory for the return status code from the process which is
+ * going to be launched by the system function. We want to share the returnstatus
+ * variable between this process and the child that is going to be created in the
+ * fork_system method.
+ * The goal is to store in this variable the return status code received from the
+ * process launched with the system method by the child process, then the parent
+ * process can retrieve that return status code and send it by TCP to the Java client.
+ * There are not concurrency issues because the parent process will just try to read
+ * this variable when the child process is dead, taking in that moment its last value
+ * and sending it to the Java client.
+ */
+
+ /*the /bin/ls must exist otherwise this does not work... */
+ keyvalue=ftok("/bin/ls", SHAREMEMKEY);
if (keyvalue == -1) {
syslog (LOG_ERR, "ftok failed: %m");
goto end_destroy_sem;
int fork_system(int socket, char *command, sem_t *semaphore, int *returnstatus)
{
- int pid;
- int out[2], err[2]; /*Store pipes file descriptors. Write ends attached to the stdout and stderr streams.*/
+ int pid; /*Child or parent PID.*/
+ int out[2], err[2]; /*Store pipes file descriptors. Write ends attached to the stdout*/
+ /*and stderr streams.*/
char buf[2000]; /*Read data buffer.*/
- char string[100];
- /*We are going to use a poll in order to find out if there are data coming from the*/
- /*pipes attached to the stdout and stderr streams.*/
- struct pollfd polls[2];
- int n;
+ char string[3000];
+ struct pollfd polls[2]; /*pipes attached to the stdout and stderr streams.*/
+ int n; /*characters number from stdout and stderr*/
int childreturnstatus;
- int returnValue = 0; /*return value from this function can be caught by upper layers, OK by default*/
+ int returnValue = 0; /*return value from this function can be caught by upper layers,*/
+ /*OK by default*/
/*Value by default*/
out[0] = out[1] = err[0] = err[1] = -1;
- /*Creating the pipes, they will be attached to the stderr and stdout streams*/
+ /*Creating pipes, they will be attached to the stderr and stdout streams*/
if (pipe(out) < 0 || pipe(err) < 0) {
syslog (LOG_ERR, "pipe failed: %m");
goto err;
exit(-1);
}
- /*TODO: I should use execve with setlocale instead of system.*/
+ /*TODO: I should use execve with setlocale and the environment instead of system.*/
/*During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT*/
/* will be ignored. From man system(3)*/
*returnstatus=system(command);
polls[0].fd=out[0];
polls[1].fd=err[0];
polls[0].events = polls[1].events = POLLIN;
- sprintf(string,"<?xml version=\"1.0\"?>");
- send(socket,string,strlen(string),0);
- sprintf(string,"<streams>");
- send(socket,string,strlen(string),0);
+
+ bzero(string, sizeof(string));
+ /*TODO: stop using XML. Next improvements: my own client/server protocol*/
+ sprintf(string,"<?xml version=\"1.0\"?><streams>");
+
+ if (TEMP_FAILURE_RETRY(send(socket,string,strlen(string),0)) < 0) {
+ syslog (LOG_INFO, "error while sending xml header: %m");
+
+ if (kill(pid, SIGKILL /*should I use SIGTERM and my own handler?*/) < 0) {
+ /*We are not sure if the child process will die. In this case, probably the child */
+ /*process is going to be an orphan and its system process (if there is one) as well*/
+ syslog (LOG_ERR, "error while killing child process: %m");
+ goto err;
+ }
+
+ if (TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)) < 0) {
+ /*We are not sure if the child process is dead. In this case, probably the child */
+ /*process is going to be an orphan and its system process (if there is one) as well*/
+ syslog (LOG_ERR, "error while waiting for killed child process: %m");
+ }
+
+ /*In Java the client will get a XMLParser Exception.*/
+ goto err;
+ }
/*Releasing barrier, the child process can keep running*/
if (sem_post(semaphore) < 0 ) {
- syslog (LOG_ERR, "parent error releasing barrier: %m");
- /*TODO: SIGTERM and SIGCHLD by default are ignored (interactive bash) */
/*if the child process launched the system command the child process will die */
/*and the system process is going to be an orphan process... :( */
+ syslog (LOG_ERR, "parent error releasing barrier: %m");
+
if (kill(pid, SIGKILL /*should I use SIGTERM and my own handler?*/) < 0) {
+ /*We are not sure if the child process will die. In this case, probably the child */
+ /*process is going to be an orphan and its system process (if there is one) as well*/
syslog (LOG_ERR, "error while killing child process: %m");
- /*This is the worst that could happen, we are not sure if the child process is going to die */
- /*in this case, probably the child process is going to be an orphan and its system process ass welll*/
- /*goto err?*/
+ goto err;
+ }
+
+ if (TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)) < 0) {
+ /*We are not sure if the child process is dead. In this case, probably the child */
+ /*process is going to be an orphan and its system process (if there is one) as well*/
+ syslog (LOG_ERR, "error while waiting for killed child process: %m");
}
- /*TODO: sigprocmask(SIG_BLOCK, SIGCHLD, NULL) at the beginning of this whole program */
- /*from bash it makes a clone and this parent process inherits the bash signal handlers */
- /*but not its signal masks. we want to block until child process is dead */
- /*because of that we must block SIGCHLD.*/
- waitpid(pid, NULL, 0);
- /*goto err?*/;
+
+ /*In Java the client will get a XMLParser Exception.*/
+ goto err;
}
while(1) {
if(poll(polls,2,100)) {
if(polls[0].revents && POLLIN) {
bzero(buf,2000);
- n=read(out[0],buf,1990);
- sprintf(string,"<out><![CDATA[");
- send(socket,string,strlen(string),0);
- send(socket,buf,n,0);
- sprintf(string,"]]></out>");
- send(socket,string,strlen(string),0);
+ bzero(string, sizeof(string));
+ n=TEMP_FAILURE_RETRY(read(out[0],buf,1990));
+ sprintf(string,"<out><![CDATA[%s]]></out>", buf);
+ if (TEMP_FAILURE_RETRY(send(socket,string,strlen(string),0)) < 0)
+ syslog (LOG_INFO, "error while sending stdout: %m");
}
if(polls[1].revents && POLLIN) {
bzero(buf,2000);
- n=read(err[0],buf,1990);
- sprintf(string,"<error><![CDATA[");
- send(socket,string,strlen(string),0);
- send(socket,buf,n,0);
- sprintf(string,"]]></error>");
- send(socket,string,strlen(string),0);
+ bzero(string, sizeof(string));
+ n=TEMP_FAILURE_RETRY(read(err[0],buf,1990));
+ sprintf(string,"<error><![CDATA[%s]]></error>", buf);
+ if (TEMP_FAILURE_RETRY(send(socket,string,strlen(string),0)) < 0)
+ syslog (LOG_INFO, "error while sending stderr: %m");
}
if(!(polls[0].revents && POLLIN) && !(polls[1].revents && POLLIN)) {
syslog (LOG_ERR, "parent error polling pipes: %m");
- /*TODO: Should I implement a SIGCHILD handler? */
- /*TODO: Should I have a SIGTERM handler in the child process? */
- kill(pid, SIGTERM);
- /*I want to send an error status to the remote calling process */
- /*TODO: Before giving this value I should make sure the child process is dead */
- /* otherwise I could finish having in *returnstatus the value from the child process */
- (*returnstatus) = -1;
- break;
+
+ if (kill(pid, SIGKILL /*should I use SIGTERM and my own handler?*/) < 0) {
+ /*We are not sure if the child process will die. In this case, */
+ /*probably the child process is going to be an orphan and its */
+ /*system process (if there is one) as well*/
+ syslog (LOG_ERR, "error while killing child process: %m");
+ goto err;
+ }
+
+ if (TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)) < 0) {
+ /*We are not sure if the child process is dead. In this case, */
+ /*probably the child process is going to be an orphan and its */
+ /*system process (if there is one) as well*/
+ syslog (LOG_ERR, "error while waiting for killed child process: %m");
+ }
+
+ /*In Java the client will get a XMLParser Exception.*/
+ goto err;
}
}
else {
/*When timeout*/
- if(waitpid(pid, &childreturnstatus, WNOHANG)) {
+ if(TEMP_FAILURE_RETRY(waitpid(pid, &childreturnstatus, WNOHANG))) {
/*Child is dead, we can finish the connection*/
/*First of all, we check the exit status of our child process*/
/*In case of error send an error status to the remote calling process*/
}
}
/*Reaching this code when child finished or if error while polling pipes*/
- sprintf(string,"<ret><![CDATA[%d]]></ret>", (*returnstatus));
- send(socket,string,strlen(string),0);
- sprintf(string,"</streams>");
- send(socket,string,strlen(string),0);
-
+ bzero(string, sizeof(string));
+ sprintf(string,"<ret><![CDATA[%d]]></ret></streams>", (*returnstatus));
+ if (TEMP_FAILURE_RETRY(send(socket,string,strlen(string),0)) < 0)
+ syslog (LOG_INFO, "error while sending return status: %m");
/*Stuff just done by the Parent process. The child process ends with exit*/
end:
return;
}
- if (signal (SIGINT, SIG_IGN) == SIG_ERR)
- syslog (LOG_ERR, "signal SIGINT desactivation failed: %m");
closeSafely (sockfd);
- /*TODO: kill child processes and release allocate memory*/
+ /*TODO: kill child processes, finish threads and release allocate memory*/
exit (0);
}