I'm writing daemon with ability to recover work process CentOS release 5.7 (Final).
Here is example of code:
#define CHILD_NEED_WORK 1
#define CHILD_NEED_TERMINATE 2
int ReloadConfig()
{
....
return 0;
}
void DestroyWorkThread()
{...}
int InitWorkThread()
{
...
return 0;
}
int LoadConfig(char* FileName)
{
...
return 0;
}
void SetPidFile(char* Filename)
{
FILE* f;
f = fopen(Filename, "w+");
if (f)
{
fprintf(f, "%u\n", getpid());
fclose(f);
}
}
int SetFdLimit(int MaxFd)
{
struct rlimit lim;
int status;
lim.rlim_cur = MaxFd;
lim.rlim_max = MaxFd;
status = setrlimit(RLIMIT_NOFILE, &lim);
return status;
}
//Monitor process
int MonitorProc()
{
int pid;
int status;
int need_start = 1;
sigset_t sigset;
siginfo_t siginfo;
parent_pid = getpid();
sigemptyset(&sigset);
sigaddset(&sigset, SIGQUIT);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGCHLD);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
SetPidFile(PID_FILE);
for (;;)
{
if (need_start)
{
pid = fork();
}
need_start = 1;
if (pid == -1)
{
}
else if (!pid)
{
status = WorkProc();
exit(status);
}
else
{
sigwaitinfo(&sigset, &siginfo);
if (siginfo.si_signo == SIGCHLD)
{
wait(&status);
status = WEXITSTATUS(status);
if (status == CHILD_NEED_TERMINATE)
{
Write("[MONITOR] Child stopped");
break;
}
else if (status == CHILD_NEED_WORK)
{
Write("[MONITOR] Child restart");
}
}
else if (siginfo.si_signo == SIGUSR1)
{
kill(pid, SIGUSR1);
need_start = 0;
}
else if (siginfo.si_signo == 0)
{
need_start = 0;
continue;
}
else
{
Write("[MONITOR] Signal ", strsignal(siginfo.si_signo));
kill(pid, SIGTERM);
status = 0;
break;
}
}
}
Write("[MONITOR] Stop");
unlink(PID_FILE);
return status;
}
//Work process
int WorkProc()
{
struct sigaction sigact;
sigset_t sigset;
int signo;
int status;
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = signal_error_for_backtrace;
sigemptyset(&sigact.sa_mask);
sigaction(SIGFPE, &sigact, 0);
sigaction(SIGILL, &sigact, 0);
sigaction(SIGSEGV, &sigact, 0);
sigaction(SIGBUS, &sigact, 0);
sigemptyset(&sigset);
sigaddset(&sigset, SIGQUIT);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
SetFdLimit(FD_LIMIT);
status = InitWorkThread();
if (!status)
{
for (;;)
{
sigwait(&sigset, &signo);
if (signo == SIGUSR1)
{
status = ReloadConfig();
if (status)
{
Write("[DAEMON] Reload config failed");
}
else
{
Write("[DAEMON] Reload config OK");
}
}
else
{
break;
}
}
DestroyWorkThread();
}
else
{
Write("[DAEMON] Create work thread failed");
}
Write("[DAEMON] Stopped");
return CHILD_NEED_TERMINATE;
}
int main(int argc , char *argv[])
{
if (argc != 2)
{
printf("Usage: ./test_daemon.conf failed!\n");
return -1;
}
status = LoadConfig(argv[1]);
if (status)
{
printf("Error: Load config failed\n");
return -1;
}
if (CheckForAnotherInstance())
{
printf("Daemon is already running!\n");
return 1;
}
pid = fork();
if (pid == -1)
{
printf("Error: Start Daemon failed (%s)\n", strerror(errno));
return -1;
}
else if (!pid)
{
umask(0);
setsid();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//Monitor process startup
status = MonitorProc();
return status;
}
else
{
return 0;
}
return 0;
}
I use two processes: work process, which produces main work and monitor process, which waits for signals from work process, and restarts them, if it receives required signal. When i try to send a signal to parent process - monitor process - with command kill -s SIGCHLD, it receives this signal.
When i try to terminate child process, parent process doesn't receive SIGCHLD signal- it contunies to wait for signals, and child process transforms to zombie.
But when i use utility strace with parent process, all works fine - child process terminates successfully, and parent process receives SIGCHLD signal.
I read about function waitpid(), which uses to receive SIGCHLD signal, but i want to receive another signals in parent process too.
Any ideas?
My guess , signal handler is not installed before first fork?
You SIG_BLOCK the SIGCHLD so will no receive any signals. But this is ok as you go on to use sigwaitinfo() but you fail to use siginfo.si_pid when you do a wait(), you should use waitpid() for the PID you are cleaning up due to receiving the signal synchronously via sigwaitinfo().
You use WEXITSTATUS() without checking WIFEXITED(status) first. See wait() man page.
Your monitor and work process appear to use the same executable as you do a fork() with out exec() after. So be careful as you may been to restore the signal handler state in the child to get the code in the child to behave normally.
For example the monitor process is the parent? So to get a child it does a fork() and then calls into WorkProc(). Inside WorkProc() it proceeds to block a bunch of signals (but not SIGCLD). However the execution is the sigprocmask(SIG_BLOCK, ...) from MonitorProc() will still be active inside WorkProc().
I am not sure what "if (siginfo.si_signo == 0) " is all about.
So to another your main query the reason why SIGCHLD is not being delivered from the process running WorkProc() function, is because you blocked that signal already inside MonitorProc(). So fix this issue use the 3rd argument to sigprocmask() to save the original block/unblock mask in MonitorProc() and when you fork() and before jumping into WorkProc() restore the block/unblock mask.
Related
Here's my attempt to create a wrapper around the winsock recv function in blocking mode so that a control-c can interrupt the connection without closing the process.
I get kind of stuck on how to do a "select" to detect if I have a "ctrl-C event" or a "socket recv event".
Any ideas on how to fix this code to get it working? The idea is that, first I call the CtrlC_Init routine to install the ctrl-c handler... then I replace recv with CtrlC_Recv in my tcpip program, and finally, i and press ctrl-c while tailing a file on a remote computer and it will magically stop without exiting my program....
#include <iostream>
#include <atomic>
#include <mutex>
#include <ws2tcpip.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
atomic<HANDLE> g_event_ctrlc {0};
BOOL WINAPI CtrlHandler(DWORD fdwCtrlType);
void CtrlC_Init()
{
g_event_ctrlc = CreateEvent(NULL, FALSE, FALSE, NULL);
SetConsoleCtrlHandler(CtrlHandler, TRUE);
}
int CtrlC_Recv( SOCKET s, char* buf, int len, int flags)
{
WSAEVENT event_recv;
HANDLE handle_array[2];
int handle_count = 0;
if (g_event_ctrlc != 0) {
handle_array[handle_count] = g_event_ctrlc;
handle_count++;
}
// Create Event Receive
event_recv = WSACreateEvent();
if (event_recv == WSA_INVALID_EVENT) {
return SOCKET_ERROR;
}
WSAEventSelect(s, event_recv, FD_READ | FD_CLOSE);
handle_array[handle_count] = event_recv;
handle_count++;
DWORD rc = WaitForMultipleObjectsEx(
handle_count,
handle_array,
FALSE, INFINITE, FALSE);
// Close Event_Recv
WSAEventSelect(s, event_recv, 0);
CloseHandle(event_recv);
// Check for Error
if(rc == WAIT_FAILED) {
DWORD err = GetLastError();
cout << "ERROR" << err << "\n";
return SOCKET_ERROR;
}
// If Event Triggered
if(rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0 + handle_count) {
// Ctrl-C Interrupt Event
if (g_event_ctrlc != 0) {
if (handle_array[rc] == g_event_ctrlc) {
cout << "CTRL-C CloseSocket\n";
closesocket(s);
return SOCKET_ERROR;
}
}
// Received Socket
if (handle_array[rc] == event_recv) {
return recv(s, buf, len, flags);
}
}
return SOCKET_ERROR;
}
BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
//NOTE: make sure console app is run from powershell.exe
// instead of being spawned by C++ IDE...
// some C++ IDEs trap Ctrl-C before it gets to console
// preventing this handler from getting triggered.
if (fdwCtrlType == CTRL_C_EVENT) {
printf("Ctrl-c\n");
if (g_event_ctrlc != 0) {
SetEvent(g_event_ctrlc);
}
return TRUE;
}
return FALSE;
}
I am trying to write a my own small linux shell , and i want to write the function cp , the function format is like the following :
cp <old-file-path> <new-file-path>
It copies the first file into the second file (overwriting it) , and if the second file doesn't exist it will create a new one.
If the files didn't open or any system call did not succeed it will print an error message.
However, sometimes I want to copy large files so I want to run this cp command in the background (using fork without waiting for it to finish).
My problem is: how can I use fork and not wait for the process to finish?
Currently, the child process becomes a zombie process.
Here is my code :
// num_args contains the number of arguments sent to cp
class CopyCommand : public BuiltInCommand {
public:
CopyCommand(const char* cmd_line) : BuiltInCommand(cmd_line){}
virtual ~CopyCommand() {}
void execute() override{
if(this->num_args < 1){ // if no arguments were send to cp
perror("invalid arguments");
return;
}
char* buff;
int fd1 = open(args[1], O_RDONLY);
if(fd1 == -1){
perror("open failed");
return;
}
if(this->num_args==2){ // copy file1 into file2 (overrite file 1)
int fd2 = open(args[2], O_TRUNC);
if (fd2 == -1) { // if we couldn't open the file then create a new one (not sure if we supposed to this ?)
fd2 = open(args[2], O_CREAT, 0666);
if (fd2 == -1) {
perror("open failed");
return;
}
}
pid_t PID = fork();
if(PID == -1){
perror("fork failed");
return;
}
else if(PID == 0){
// i need to use fork here :( before i start to write
int read_res = read(fd1, &buff, 1); /// read from the file fd1 into fd2
while (read_res != -1) {
if (!read_res) {
break;
}
if (write(fd2, buff, 1) == -1) {
perror("write failed");
}
read_res = read(fd1, buff, 1);
}
if (read_res == -1) {
perror("read failed");
}
}
}
else if(this->num_args==1){ // create file2 and copy file1 into file2
// don't know how to do this yet
// i need to use fork here :(
}
}
};
For starters, I rewrote your code a bit.
In particular, note that the child branch (PID == 0) exits when it is done.
The parent closes the passed-down file descriptors after forking and in case of error.
if (this->num_args == 2) {
int fd1 = open(args[1], O_RDONLY);
if (fd1 == -1) {
perror("open failed");
return;
}
int fd2 = open(args[2], O_TRUNC);
if (fd2 == -1) {
fd2 = open(args[2], O_CREAT, 0666);
if (fd2 == -1) {
perror("open failed");
close(fd1);
return;
}
}
pid_t PID = fork();
if (PID == -1) {
perror("fork failed");
} else if (PID == 0) {
char buff[1024];
int read_res = read(fd1, &buff, 1024); /// read from the file fd1 into fd2
while (read_res != -1) {
if (!read_res) {
break;
}
if (write(fd2, buff, read_res) == -1) {
perror("write failed");
}
read_res = read(fd1, buff, 1024);
}
if (read_res == -1) {
perror("read failed");
}
exit(0);
} else {
printf("Copy running in background (pid: %d)\n", PID);
}
close(fd1);
close(fd2);
return
}
When the child process calls exit, the process will stick around in "Zombie" state. This state allows the parent process (you) to call wait or waitpid to retrieve the exit code.
As a secondary effect of the process ending, the kernel will send a SIGCHLD to your process, to let you know you can actually call wait without blocking. In your situation, you do not care about the exit code, so you can set up a "don't care" signal handler at the start of your program and let the kernel clean up the process:
signal(SIGCHLD, SIG_IGN);
This is documented in signal(2):
If a process explicitly specifies SIG_IGN as the action for the signal
SIGCHLD, the system will not create zombie processes when children of the
calling process exit. As a consequence, the system will discard the exit
status from the child processes.
I have a function that is calling a process called driverclear. Seems like the process starts but it never returns because I never get the output of the process and I never get the "Process Complete" message. Is there something I am doing wrong?
void cleanDriver
{
pid_t pid;
if(chmod("./helpers/driverclear", S_IXUSR) == 0)
{
int status = 0;
pid = fork();
if(pid == 0)
{
if(!execl("./helpers/driverclear", "driverclear", (char*) NULL))
{
perror("execl failed.\n");
}
}
else
{
printf("Process Starting...");
waitpid(pid, &status, 0);
printf("Process Complete\n");
}
}
}
Instead of using execl I just switched to using system("sh root/helpers/driverclear"); which fixes my problems.
I want to create a deamon in Linux that needs to kill off another process. I've created the deamon and it works fine, but my second process is created but it does not run as expected.
Is there something that I'm missing in starting the new process?
This is my code
void StartWSDevice()
{
pid_t pid;
int status;
fflush(NULL);
pid = fork();
switch (pid) {
case -1:
perror("fork");
break;
case 0: {
syslog(LOG_NOTICE, "WSDevice started.");
int res = execl("home/pi/projects/WSDevice/bin/ARM/Debug",
"WSDevice.out", (char *)0);
syslog(LOG_NOTICE, "WSDevice return %d.", res);
break;
}
default:
fflush(NULL);
if (waitpid(pid, &status, 0) != -1) {
syslog(LOG_NOTICE, "Child exited with status %i\n", status);
} else {
perror("waitpid");
}
break;
}
}
int main(void) {
deamonize();
syslog(LOG_NOTICE, "WSDeviceService started.");
while (!stopService) {
// Check if my child process is running
int pid_file = open("/var/run/WSDevice.pid",
O_CREAT | O_RDWR, 0666);
int rc = flock(pid_file, LOCK_EX | LOCK_NB);
if (rc) {
if (EWOULDBLOCK == errno) {
}
} else {
StartWSDevice(); // Its not running, start it
}
sleep(30); /* wait 30 seconds */
}
syslog(LOG_NOTICE, "WSDeviceService terminated.");
exit(EXIT_SUCCESS);
}
You're using execl incorrectly. The first argument to execl() is the process to run. The remaining arguments are the contents of the argv array that is passed to the process. The key here is that argv[0] should be the name of the process being run. So:
int res = execl("/home/pi/projects/WSDevice/bin/ARM/Debug/WSDevice.out",
"/home/pi/projects/WSDevice/bin/ARM/Debug/WSDevice.out",
NULL);
Note that I've also inserted / in front of home. This may be important for you. I've also used NULL instead of (char *)0, which is more of a style thing and doesn't change the functionality.
In my C++ application, I am seeing a pclose() that hangs because the pipe's process hung and never exited. Is there anyway I could do something like select() to test whether the pclose() will return because the child process has completed? I'd rather not do a fork() instead of popen() if possible. If fork() is the only solution, are there any examples of using fork() to replace a popen() / pclose() scenario?
Probably the easiest way, particularly if you only have one child process, is to catch SIGCHLD and set a flag that the process has terminated and pclose() can be called.
Here's a simple example:
sillyprog.c:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("This is some data from the child.\n");
fflush(stdout);
sleep(5);
return 0;
}
pc.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
volatile sig_atomic_t child_done = 0;
void handler(int signum)
{
if ( signum == SIGCHLD ) {
child_done = 1;
}
}
int main(void)
{
/* Set signal handler */
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if ( sigaction(SIGCHLD, &sa, NULL) == -1 ) {
perror("couldn't set signal handler");
return EXIT_FAILURE;
}
/* Open pipe */
FILE * fp = popen("./sillyprog", "r");
if ( !fp ) {
fprintf(stderr, "Couldn't open pipe\n");
return EXIT_FAILURE;
}
/* Get a line from pipe */
char buffer[100];
if ( !fgets(buffer, 100, fp) ) {
fprintf(stderr, "Error calling fgets()\n");
return EXIT_FAILURE;
}
const size_t len = strlen(buffer);
if ( len && buffer[len - 1] == '\n' ) {
buffer[len - 1] = 0;
}
printf("Got '%s' from pipe.\n", buffer);
/* Wait for child to finish */
while ( !child_done ) {
printf("Child not ready, waiting...\n");
sleep(1);
}
/* Close pipe */
if ( pclose(fp) == -1 ) {
fprintf(stderr, "Error calling pclose()\n");
return EXIT_FAILURE;
}
else {
printf("pclose() successfully called.\n");
}
return 0;
}
which outputs:
paul#horus:~/src/sandbox$ ./pc
Got 'This is some data from the child.' from pipe.
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
pclose() successfully called.
paul#horus:~/src/sandbox$