I have a multi-process program that receives a file as input from the command line. Creates shared memory, and forks for a producer/consumer styled problem. It switches every other byte making abcdef badcef. However, I had not realized this must also allow for non-ASCII input, and I do not know how to do this. I believe wchar_t allows for non-ASCII input. But what about my functions such as fgets/strcpy that do not work with wchar_t data type? I cannot find supported function replacements.
Please don't do the entire problem for me, this is an assignment for my class but general advice is greatly appreciated.
I've tried using wchar_t in place of char. I tried to use fgetsws in place of fgets but even when include the compiler doesn't recognize fgetsws as a library function. I'm running this on ubuntu linux 64 bit machine.
pa2.h================================================
#ifndef pa2_h
#define pa2_h
int shflag;
sigset_t new, old, nomask;
struct shbuff
{
char data[1024];
int size;
};
void flagfunc();
void block();
void sortstring(char input[]);
void waitchild();
void waitparent();
void parentsig(pid_t pid);
void childsig(pid_t pid);
#endif
pa2.c==========================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#include "pa2.h"
int main(int argc, char* argv[])
{
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pid_t pid;
int mem;
FILE* fpinput;
FILE* fpout;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
char buff[1024]; // Do I conver this to wchar_t data type?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
key_t shkey;
void *ptr =0;
struct shbuff* shmem;
if(argc < 2)
{
printf("Input a file name including .txt!");
exit(1);
}
block(); //block sigusr1
pid=fork(); // child gets pid 0, parent is positive int
if(pid != 0) // parent process
{
fpinput= fopen(argv[1], "r"); // read from file
mem= shmget(shkey, sizeof(struct shbuff), IPC_CREAT | 0666); //size rounded up to nearest page size / 0666 required for server read/write
ptr= shmat(mem, (void *)0, 0); // Attach the shared memory
shmem= (struct shbuff *) ptr;
shmem->size= 0;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
while (fgets(buff,1023,fpinput) != NULL) //read in from file to buffer leaving room for terminating character
// Do I use fgetsws to replace this fgets?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
pthread_mutex_lock(&mutex1);
sortstring(buff); //switch bytes in the buffer
pthread_mutex_unlock(&mutex1);
while (shmem->size != 0)
{
waitchild(); // wait for child to read from buff
}
pthread_mutex_lock(&mutex1);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
strcpy(shmem->data, buff); //replacement for strcpy?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock(&mutex1);
shmem->size= strlen(shmem->data); //set size to strlen of data
pthread_mutex_unlock(&mutex1);
childsig(pid); // signal to parent
}
while (shmem->size != 0)
{
waitchild(); // wait for child to read from buff
}
shmem->size= -1; //end of file
childsig(pid); //send signal
fclose(fpinput); //close file
shmdt(shmem); // detach from shared memory
printf("Transferred.\n\n");
} //end of else if (pid != 0)
else //if pid is 0 then process the child
{
fpout= fopen("output.txt", "w");
mem= shmget(shkey, sizeof(struct shbuff), IPC_CREAT | 0666); //size rounded up to nearest page size / 0666 required for server read/write
ptr= shmat(mem, (void *)0, 0); // attach to shared memory
shmem= (struct shbuff*) ptr; //convert the sharedMemory into struct
while (shmem->size != -1) // while not EOF
{
while (shmem->size == 0)
{
waitparent(); //wait for the child to read from shared memory
}
if (shmem->size != -1) //if not EOF
{
fputs(shmem->data,fpout); //copy the contents into the outFile
shmem->size = 0; //re-zero size after emptying data buffer
parentsig(getppid()); //parent signal
}
}
fclose(fpout); // closes output file
shmdt(shmem); // detach shared memory
kill(getpid(),SIGTERM); // Kills this process politely with sigterm rather than sigkill, We are civilized
}
exit(0);
}
void flagfunc()
{
shflag = 1;
}
void sortstring(char input[]){
char temp;
int i= 0;
for(i=0; i<strlen(input); i+=2){
temp= input[i];
input[i]= input[i+1];
input[i+1]= temp;
}
}
void block()
{
signal(SIGUSR1,flagfunc);
sigemptyset(&nomask); //empty mask
sigemptyset(&new);
sigaddset(&new, SIGUSR1); //add sigusr1 signal to mask
sigprocmask(SIG_BLOCK,&new,&old); //block new/old signals
}
void waitchild()
{
while (shflag==0)
{
sigsuspend(&nomask); //wait on child signal
}
shflag=0; //reset flag
sigprocmask(SIG_SETMASK,&old,NULL);
}
void waitparent()
{
while (shflag==0)
{
sigsuspend(&nomask); //wait for parent
}
shflag=0; //reset flag
sigprocmask(SIG_SETMASK,&old,NULL);
}
void childsig(pid_t pid)
{
kill(pid,SIGUSR1); //sends parent signal
}
void parentsig(pid_t pid)
{
kill(pid,SIGUSR1); //sends child signal
}
Related
Referring to following code example, I want the main thread to supply the number num that the child thread is expecting using scanf.
I tried this way to write the wordcount (9) to stdin which is to be read by child thread, but it is not working.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
sleep(2);
char buffer[50];
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
int save_stdin = dup(fileno(stdin));
dup2(fileno(wfp), fileno(stdin));
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
dup2(save_stdin, fileno(stdin));
pclose(wfp);
}
pthread_join(tid, NULL);
}
Can someone suggest a correct way or any other alternative way to do this?
Thanks.
I don't think there is any good way for a process to write text to its own stdin; stdin is meant to be a way for the parent process (or the user, if the parent process is a Terminal window) to send data to your process, not for your process to send data to itself.
However, you could achieve a similar result by having your child thread use select() or similar to read input from both stdin and from the output end of a pipe; then your parent process can send data to the child process by writing to the input end of that same pipe.
Below is a modified version of your program demonstrating the technique. Note that the child thread will print out any text that you type into stdin; and also the main thread will send a line of text to the child thread once every 5 seconds, and the child thread will also print out that text. After the main thread has sent 5 messages to the child thread, the main thread will close its end of the pipe, causing the child thread to exit and then the process can exit cleanly as well.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
static int pipeReadFD = -1;
static int ReadTextFrom(const char * descriptionOfSender, int fd)
{
char buf[256];
const int numBytesRead = read(fd, buf, sizeof(buf)-1); // -1 so we always have room to place NUL terminator byte
if (numBytesRead > 0)
{
buf[numBytesRead] = '\0'; // make sure the string is NUL-terminated
printf("ReadTextFrom(): Read %i bytes from [%s]: [%s]\n", numBytesRead, descriptionOfSender, buf);
}
return numBytesRead;
}
void* init_on_sys_ready(void* terminalflag)
{
int num=0;
printf("Child thread: trying to read text from stdin\n");
while(1)
{
const int stdinFD = fileno(stdin);
const int maxFD = (pipeReadFD > stdinFD) ? pipeReadFD : stdinFD;
fd_set readFDSet;
FD_ZERO(&readFDSet);
FD_SET(stdinFD, &readFDSet);
FD_SET(pipeReadFD, &readFDSet);
const int selRet = select(maxFD+1, &readFDSet, NULL, NULL, NULL);
if (selRet >= 0)
{
if ((FD_ISSET(stdinFD, &readFDSet))&&(ReadTextFrom("stdin", stdinFD) <= 0)) break;
if ((FD_ISSET(pipeReadFD, &readFDSet))&&(ReadTextFrom("pipe", pipeReadFD) <= 0)) break;
}
else
{
perror("select");
break;
}
}
printf("Child thread exiting!\n");
return NULL;
}
int main(int argc, char ** argv)
{
int pipeFDs[2];
if (pipe(pipeFDs) < 0)
{
perror("pipe");
return -1;
}
pipeReadFD = pipeFDs[0];
int pipeWriteFD = pipeFDs[1];
pthread_t tid;
if (pthread_create(&tid, NULL, &init_on_sys_ready, NULL) != 0) {
printf("Failed to initialize CLI\n");
exit(1);
}
int count = 0;
for (int count=0; count < 5; count++)
{
char buf[512];
snprintf(buf, sizeof(buf), "Hello #%i from main thread", ++count);
const size_t slen = strlen(buf);
if (write(pipeWriteFD, buf, slen) == slen)
{
printf("main() sent [%s] to the child thread via the pipe.\n", buf);
}
else
{
perror("write");
break;
}
sleep(5);
}
close(pipeWriteFD); // this will cause the child thread to exit ASAP
pthread_join(tid, NULL);
return 0;
}
popen's man states:
[...] the command's standard output is the same as that of the process that called popen()
So you just need a way to redirect stdout to stdin.
Which is exactly what pipe is for. It links an output fd with an input fd.
As pipe creates new fds, we need to use dup2 to replace stdin and stdout, as you've already did in your example code. Threads share the same memory, so you don't have to worry about any child/parent differences in fds.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
setbuf(stdin, NULL);
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
int save_stdin = dup(STDIN_FILENO);
int save_stdout = dup(STDOUT_FILENO);
int tube[2];
pipe(tube);
dup2(tube[0], STDIN_FILENO);
dup2(tube[1], STDOUT_FILENO);
char buffer[50] = {0};
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
pclose(wfp);
}
dup2(save_stdin, STDIN_FILENO);
dup2(save_stdout, STDOUT_FILENO);
pthread_join(tid, NULL);
}
I'm writing a very simple bash-like shell in C and am currently implementing pipes between commands (i.e. command1 | command2, which should run both commands at the same time with the stdout of the first one connected through a pipe with the stdin of the second one).
I've gotten to the point where something like
shell> echo test | cat | cat
correctly prints "test" to the string, but anything more complicated than that doesn't make it. For example:
shell> ls -1 / | sort | rev
It's (as far as I can tell) equivalent to the previous one in terms of piping, yet this one fails and the other one succeeds.
I'm at a complete loss as to why this is because I've debugged both the main process and the children exhaustively and verified that the processes get launched with the correct connections both in the working and in the not working command.
Here's a simplified version of the code:
// Uncomment to use hardcoded input
// #define USE_HARDCODED_INPUT
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // NULL
#include <errno.h> // ENOENT
#include <stdio.h> // setbuf, printf
#include <unistd.h> // exec, fork
#include <fcntl.h> // open
#include <sys/types.h> // wait
#include <sys/wait.h>
void set_process_FDs(int input, int output, int error)
{
if (input)
{
dup2(input, STDIN_FILENO);
close(input);
}
if (output)
{
dup2(output, STDOUT_FILENO);
close(output);
}
if (error)
{
dup2(error, STDERR_FILENO);
close(error);
}
}
void child_setup(char **argv, int input, int output, int error)
{
if (input || output || error)
set_process_FDs(input, output, error);
execvp(argv[0], argv);
perror("exec()");
exit(1);
}
int launch_process(char **argv, int is_last,
int input, int output, int error)
{
int status;
pid_t pid = fork();
switch(pid)
{
case -1:
perror("fork()");
return 0;
case 0:
child_setup(argv, input, output, error);
return 0;
default:
break;
}
if (is_last)
wait(&status);
return 1;
}
int run_commands(char ***argvv)
{
int no_commands_ran = 0;
int argc;
char **argv = argvv[0];
int in_pipe[2];
int out_pipe[2];
for (int i=0; (argv = argvv[i]); ++i)
{
pipe(out_pipe);
if (i == 0)
in_pipe[0] = 0;
if (!argvv[i+1])
{
close(out_pipe[0]);
close(out_pipe[1]);
out_pipe[1] = 0;
}
for (argc=0; argv[argc]; ++argc);
if (!launch_process(argv, !argvv[i+1],
in_pipe[0], out_pipe[1], 0))
break;
if (i != 0)
{
close(in_pipe[0]);
close(in_pipe[1]);
}
in_pipe[0] = out_pipe[0];
in_pipe[1] = out_pipe[1];
no_commands_ran = i + 1;
}
return no_commands_ran;
}
extern int obtain_order(); // Obtains an order from stdin
int main(void)
{
char ***argvv = NULL;
int argvc;
char *filev[3] = {NULL, NULL, NULL};
int bg;
int ret;
setbuf(stdout, NULL); // Unbuffered
setbuf(stdin, NULL);
while (1)
{
#ifndef USE_HARDCODED_INPUT
printf("%s", "shell> "); // Prompt
ret = obtain_order(&argvv, filev, &bg);
if (ret == 0) // EOF
{
fprintf(stderr, "EOF\n");
break;
}
if (ret == -1)
continue; // Syntax error
argvc = ret - 1; // Line
if (argvc == 0)
continue; // Empty line
if (!run_commands(argvv))
continue; // Error executing command
#else
argvc = 3;
char ***argvv1 = calloc(4, sizeof(char*));
argvv1[0] = calloc(3, sizeof(char*));
argvv1[0][0] = strdup("echo");
argvv1[0][1] = strdup("test");
argvv1[1] = calloc(2, sizeof(char*));
argvv1[1][0] = strdup("cat");
argvv1[2] = calloc(2, sizeof(char*));
argvv1[2][0] = strdup("cat");
char ***argvv2 = calloc(4, sizeof(char*));
argvv2[0] = calloc(4, sizeof(char*));
argvv2[0][0] = strdup("ls");
argvv2[0][1] = strdup("-1");
argvv2[0][2] = strdup("/");
argvv2[1] = calloc(4, sizeof(char*));
argvv2[1][0] = strdup("sort");
argvv2[2] = calloc(4, sizeof(char*));
argvv2[2][0] = strdup("rev");
printf("%s", "shell> echo test | cat | cat\n");
if (!run_commands(argvv1))
continue; // Error executing command
usleep(500);
printf("%s", "shell> ls -1 / | sort | rev\n");
if (!run_commands(argvv2))
continue; // Error executing command
printf("%s", "\nNo more hardcoded commands to run\n");
break;
#endif
}
return 0;
}
obtain_order() is a function located in the parser, which is a simple Yacc parser. It just fills the vector of argvs called argvv with whatever was input in the shell. In case anyone wants to try the code and see the problem, simply uncomment the #define at the beginning to see the behaviour you'd get from typing the problematic commands manually.
To start, your parent process does not wait for all of its child processes to complete their execution.
This call to wait does occur after the last child process has been spawned
if (is_last)
wait(&status);
but it does not necessarily wait for the last child process. That is to say, it will return when any one child process has completed execution (or an error occurs).
Properly waiting for all child processes to complete, at the end of run_commands,
/* ... */
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
return no_commands_ran;
exposes the fact that children after the first are hanging, as wait blocks execution of the parent program.
(After placing a few fprintf statements. █ here indicates program is blocking.)
shell> echo test | cat | cat
LOG: Child<30607> (echo)
LOG: Child<30608> (cat)
LOG: Child<30609> (cat)
LOG: Child<30607> process exited with status <0>
█
Without waiting for all child processes, you are creating orphan processes.
As for why these processes fail to terminate, this is due to the fact that certain file descriptors are not being closed.
The call to launch_process
launch_process(argv, !argvv[i+1], in_pipe[0], out_pipe[1], 0)
ensures that in_pipe[0] and out_pipe[1] are closed in the child process, but leaks any valid file descriptors in_pipe[1] or out_pipe[0]. With those leaked file descriptors still open in the child processes, the associated pipes remain valid, and thus the processes will continue to block while they wait for more data to arrive.
The quickest fix is to change launch_process to accept both pipes
int launch_process(char **argv, int is_last,
int input[2], int output[2], int error);
pass both pipes
if (!launch_process(argv, !argvv[i+1], in_pipe, out_pipe, 0))
close the excess file descriptors
case 0:
close(input[1]);
close(output[0]);
child_setup(argv, input[0], output[1], error);
return 0;
remove
if (is_last)
wait(&status);
and add the previously shown wait loop to the end of run_commands.
Here is a complete example of a working version of your program, with minimal refactoring.
Compile with -DDEBUG for some additional sleep time, in order to discover file descriptor leaks (there should not be any). Please read the extended comment in main.
#define _POSIX_C_SOURCE 200809L
#define USE_HARDCODED_INPUT
#define DEBUG_SLEEP_TIME 20
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
void set_process_FDs(int input, int output, int error)
{
if (input) {
dup2(input, STDIN_FILENO);
close(input);
}
if (output) {
dup2(output, STDOUT_FILENO);
close(output);
}
if (error) {
dup2(error, STDERR_FILENO);
close(error);
}
}
void child_setup(char **argv, int input, int output, int error)
{
if (input || output || error)
set_process_FDs(input, output, error);
#ifdef DEBUG
/* a sleep here should allow time to inspect
* `/proc/$PID/fd` for FD leaks, see `main` for details
* if the child process hangs you will have ample time, regardless
*/
sleep(DEBUG_SLEEP_TIME);
#endif
execvp(argv[0], argv);
perror("exec()");
exit(EXIT_FAILURE);
}
int launch_process(char **argv, int is_last,
int input[2], int output[2], int error)
{
pid_t pid = fork();
(void) is_last;
switch(pid) {
case -1:
perror("fork()");
return 0;
case 0:
fprintf(stderr, "LOG: Child<%ld> (%s)\n", (long) getpid(), *argv);
close(input[1]);
close(output[0]);
child_setup(argv, input[0], output[1], error);
return 0;
default:
break;
}
return 1;
}
int run_commands(char ***argvv)
{
int no_commands_ran = 0;
int in_pipe[2];
int out_pipe[2];
char **argv;
for (int i = 0; (argv = argvv[i]); ++i) {
pipe(out_pipe);
if (i == 0)
in_pipe[0] = 0;
if (!argvv[i+1]) {
close(out_pipe[0]);
close(out_pipe[1]);
out_pipe[1] = 0;
}
if (!launch_process(argv, !argvv[i+1], in_pipe, out_pipe, 0))
break;
if (i != 0) {
close(in_pipe[0]);
close(in_pipe[1]);
}
in_pipe[0] = out_pipe[0];
in_pipe[1] = out_pipe[1];
no_commands_ran = i + 1;
}
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
return no_commands_ran;
}
int main(void)
{
fprintf(stderr, "LOG: Parent ID: <%ld>\n", (long) getpid());
#ifdef USE_HARDCODED_INPUT
char ***argvv1 = calloc(4, sizeof(char*));
argvv1[0] = calloc(3, sizeof(char*));
argvv1[0][0] = "echo";
argvv1[0][1] = "test";
argvv1[1] = calloc(2, sizeof(char*));
argvv1[1][0] = "cat";
argvv1[2] = calloc(2, sizeof(char*));
argvv1[2][0] = "cat";
char ***argvv2 = calloc(4, sizeof(char*));
argvv2[0] = calloc(4, sizeof(char*));
argvv2[0][0] = "ls";
argvv2[0][1] = "-1";
argvv2[0][2] = "/";
argvv2[1] = calloc(2, sizeof(char*));
argvv2[1][0] = "sort";
argvv2[2] = calloc(2, sizeof(char*));
argvv2[2][0] = "rev";
puts("shell> echo test | cat | cat");
if (!run_commands(argvv1))
return EXIT_FAILURE;
/* usleep is deprecated */
nanosleep(&(struct timespec) { .tv_nsec = 5e5 }, NULL);
puts("shell> ls -1 / | sort | rev");
if (!run_commands(argvv2))
return EXIT_FAILURE;
puts("No more hardcoded commands to run");
#endif
#ifdef DEBUG
/* compile with -DDEBUG
* placing a sleep here to provide time to discover
* any file descriptor leaks
* inspect `ls -l /proc/$PID/fd`
* only the standard stream fds should exist (0, 1, 2) at
* either debug sleep
* see child_setup as well
*/
sleep(DEBUG_SLEEP_TIME);
#endif
}
Here is a cursory, annotated example of establishing a series of pipes and processes. It works similarly to your example, and might help to further showcase the order in which file descriptors must be opened, duplicated, and closed.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int valid(int fd)
{
return fd >= 0;
}
/* these safe_* functions are a non-operation when passed negative values */
void safe_close(int fd)
{
if (valid(fd) && !valid(close(fd)))
perror("close");
}
void safe_dup2(int old, int new)
{
if (valid(old) && valid(new) && !valid(dup2(old, new)))
perror("dup2");
}
void execute(char *args[][8], size_t length)
{
int channel[2] = { -1, -1 };
for (size_t i = 0; i < length; i++) {
/* get previous reader in parent */
int from = channel[0];
/* close previous writer in parent */
safe_close(channel[1]);
/* create current-writer-to-next-reader pipe */
if (!valid(pipe(channel)))
perror("pipe");
int to = (i < length - 1) ? channel[1] : -1;
if (0 == fork()) {
/* duplicate previous reader to stdin in child */
safe_dup2(from, fileno(stdin));
/* close previous reader in child */
safe_close(from);
/* close next reader in current child */
safe_close(channel[0]);
/* duplicate current writer to stdout in child */
safe_dup2(to, fileno(stdout));
/* close current writer in child */
safe_close(channel[1]);
execvp(args[i][0], args[i]);
perror("exec");
exit(EXIT_FAILURE);
}
/* close previous reader in parent */
safe_close(from);
}
/* close final pipe in parent */
safe_close(channel[0]);
safe_close(channel[1]);
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
}
int main(void)
{
char *argv[][8] = {
{ "echo", "test" },
{ "cat" },
{ "cat", "-n" }
};
execute(argv, 3);
char *argv2[][8] = {
{ "ls", "-1", "/" },
{ "sort" },
{ "rev" }
};
execute(argv2, 3);
}
Aside: As an edge case, 0 is a valid file descriptor. set_process_FDs is flawed in that if STDIN_FILENO is closed, and a new file descriptor is acquired, it may be zero. if (output) or if (error) may not behave as expected.
I wrote a program deamon which copy files with one folder to another .I have to implement SIGUSR1 which immediately wake up the daemon by sending him a SIGUSR1 signal. I do not know what I did wrong ,I use command kill -SIGUSR1 ,maybe wrong command?.Somebody know what is wrong with this code ?I did not have any warning after compiled this program,but just nothing happend
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
#define _XOPEN_SOURCE ;
int recursion = 0; //1 if enabled, otherwise 0
int sleepTime = 300;
int fileLimit = 0;
int signaL = 0;
int exitSignal = 0;
int buffer = 1000;
//Returns 0 if arguments are correct otherwise returns 1
int readArguments(int number, char **argv, char *source, char *goal);
int checkFileType(struct stat file);
int copy(char *source, char *target, mode_t mask);
int copy_map(char *source, char *target, struct stat *Source);
void syncCopy(char *source, char *target);
void syncRemove(char *source, char *target);
void my_handler(int sig)
{
syslog(LOG_INFO, "Daemon received signal SIGUSR1\n");
signaL = 1;
}
void exitFunction(int sig)
{
syslog(LOG_INFO, "Daemon received signal SIGUSR2\n");
exitSignal = 1;
}
int main(int argc, char **argv)
{
//char tables for paths
char source[500], goal[500];
struct stat Source, Goal;
struct sigaction my_action, old_action;
//checking and reading arguments
if (readArguments(argc, argv, source, goal) == 1)
exit(-1);
//checking paths
//checking if argv[1] and argv[2] are existing paths
if (lstat(source, &Source) != 0 || lstat(goal, &Goal) != 0) //bad result
{
printf("One of the paths or both dont exist\n");
exit(-1);
}
if (checkFileType(Source) != 0)
{
printf("Source path is not path to folder");
exit(-1);
}
if (checkFileType(Goal) != 0)
{
printf("Goal path is not path to folder");
exit(-1);
}
//forking the parent process
pid_t pid;
// Fork off the parent process and create new
pid = fork();
//if failure
if (pid < 0)
{
exit(-1);
}
// if it is native process
else if (pid > 0)
{
return 0;
}
//if pid==0 then it is childs process
//now we have to umask in order to write to any files(for exmaple logs)
umask(0);
openlog("logFile", LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "Deamon has just started running\n");
pid_t sid = setsid();
if (sid < 0)
{
syslog(LOG_ERR, "Error with session opening\n");
exit(-1);
}
//SIGNAL SIGUSR1
my_action.sa_handler = my_handler;
sigfillset(&my_action.sa_mask);
my_action.sa_flags = 0;
if (sigaction(SIGUSR1, &my_action, &old_action) < 0)
{
syslog(LOG_ERR, "Error with the use of SIGUSR1 signal\n");
exit(-1);
}
//SIGNAL SIGUSR2 for exiting daemon
my_action.sa_handler = exitFunction;
sigfillset(&my_action.sa_mask);
my_action.sa_flags = 0;
if (sigaction(SIGUSR2, &my_action, &old_action) < 0)
{
syslog(LOG_ERR, "Error with the use of SIGUSR2 signal\n");
exit(-1);
}
while (!exitSignal)
{
sleep(sleepTime);
switch (signaL)
{
case 0:
syslog(LOG_INFO, "Demon started working after %ds\n", sleepTime);
break;
case 1:
{
syslog(LOG_INFO, "Demon started working after SIGUSR1 signal\n");
signaL = 0; //Need to reeset signaL
break;
}
}
syncCopy(source, goal);
syncRemove(source, goal);
syslog(LOG_INFO, "Demon has just gone to sleep");
}
//at the end of program we need to close log using
syslog(LOG_INFO, "Demon has stopped\n");
closelog();
return 0;
}
Use command as kill -10 <pid> for SIGUSR1 and kill -12 <pid> for SIGUSR2.
kill -l // command to know the signal number.
Also make variable signaL , exitSignal as volatile sig_atomic_t type.
WHY volatile?
when a global variable updated in signal handler is periodically checked in some other function for appropriate action, we should always declare them using the volatile attribute in order to prevent the compiler from performing optimizations that result in the variable being stored in a register. In worst case, updated value of variable(updated in handler context) won't be visible to function polling for the variable.
WHY sig_atomic_t?
Reading and writing global variables may involve more than one machine- language instruction, and a signal handler may interrupt the main program in the middle of such an instruction sequence. (We say that access to the variable is nonatomic.) For this reason, the C language standards and SUSv3 specify an integer data type, sig_atomic_t, for which reads and writes are guaranteed to be atomic. Thus, a global flag variable that is shared between the main program and a signal handler should be declared as follows:
volatile sig_atomic_t signaL;
I want to delete the FIFO file when I suddenly click "ctrl+c" . I want to catch that signal and then delete the after before actually killing the process .
here is my code and I don't know what went wrong :
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#define MAX_BUF 512
void sigintHandler(int sig_num,char * myfifo)
{
puts("hello you there .please don't leave ");
unlink(myfifo);
exit(0);
}
int main()
{
printf("Access-Control-Allow-Origin: *\n");
printf("Content-Type: text/event-stream\n\n");
int fd;
char buf[MAX_BUF];
char * myfifo = "/tmp/omcipipe";
struct stat st;
//catch ctrl c
sigaction(SIGINT, sigintHandler);
//Create FIFO file
if (stat(myfifo, &st) != 0)
mkfifo(myfifo, 0666);
fd = open(myfifo, O_RDONLY);
while (read(fd, buf, MAX_BUF)>0)
{
printf("data: %s", buf);
printf("\n");
fflush(stdout);
}
puts( "----closing----");
close(fd);
unlink(myfifo);
return 0;
}
Firstly your way of setting a signal handler using sigaction() is not correct as you didn't fill all the member of struct sigaction. Fill all required member
struct sigaction info;
info.sa_handler = sigintHandler;
sigaction(SIGINT,&info,NULL);
Secondly, to read data from fifo file while loop is not required as you are reading MAX_BUF at a time. Loop is not required, read like this
int ret = read(fd, buf, MAX_BUF);
buf[ret-1] = '\0'; /*it should be null terminated */
Thirdly, sigintHandler() excepts only one argument. From the manual page of sigaction()
struct sigaction {
void (*sa_handler)(int); /*handler expects only 1 argument */
/* other member */
}
Finally & most importantly it is not safe to call functions like printf() & exit() from within a signal handler.
your sigHandler() looks like
static void sigintHandler(int sig_num) {
write(1,"in isr\n",strlen("in isr\n"));
/* set some flag variable here & use that
flag variable in main() function to remove the fifo */
}
see this How to avoid using printf in a signal handler?
Here is the example code
static int fifo_flag = 0;
static void sigintHandler(int sig_num) {
write(1,"in isr\n",strlen("in isr\n"));
fifo_flag = 1;
}
int main(void){
printf("Access-Control-Allow-Origin: *\n");
printf("Content-Type: text/event-stream\n\n");
int fd = 0, index = 0;
char buf[MAX_BUF];
#if 0
char *myfifo = "data";
#endif
//char * myfifo = "/tmp/omcipipe";
struct stat st;
struct sigaction info;
info.sa_handler = sigintHandler;
//info.sa_flags = /* set to defaulgs a/c to your requirement*/
if (stat(myfifo, &st) != 0) {
mkfifo(myfifo, 0666);
perror("mkfifo");
}
fd = open(myfifo, O_RDONLY | 0666);
if(fd == -1){
perror("open");
return 0;
}
char ch = 0;
while(read(fd, &ch, 1) > 0) {
sigaction(SIGINT,&info,NULL);/* if ctrl+c is called */
buf[index] = ch;
//sleep(1);/* just to observe ctrl+c behaviour, not advised to use */
printf("%c\n",buf[index]);
index++;
}
buf[index] = '\0';
printf("data: %s", buf);
printf("\n");
puts( "----closing----");
close(fd);
if(fifo_flag == 1) { /*if flag is set, unlink fifo */
unlink(myfifo); /* you can unlink fifo file here */
}
return 0;
}
There are numerous problems with your code.
This code assumes read() terminates data with a '\0' character:
while (read(fd, buf, MAX_BUF)>0)
{
printf("data: %s", buf);
printf("\n");
fflush(stdout);
}
read() merely reads raw bytes and nothing more. It will not properly terminate strings with a '\0' character. So the printf() will almost certainly invoke undefined behavior.
This code
sigaction(SIGINT, sigintHandler);
is just wrong. sigaction() takes three parameters:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
You also can only make async-signal-safe function calls from within a signal handler. This code
void sigintHandler(int sig_num,char * myfifo)
{
puts("hello you there .please don't leave ");
unlink(myfifo);
exit(0);
}
calls puts() and exit(), neither of which are async-signal-safe.
And as noted in the comments, the signal handler code assumes that myfifo is passed as the second parameter. It isn't. Per the sigaction man page the second parameter is a struct siginfo * that contains information regarding the signal context - but only if the struct sigaction passed to the sigaction() call that registered the signal handler had the SA_SIGINFO flag set, and the handler set using the sa_sigaction member instead of the sa_handler member.
I'm trying to pass some values between a parent and 10 child processes with shared memory and active wait.
Some values in the struct are pointers to allocate outside dynamic memory
The error is showing when i'm trying to write a number in a string to pass a path file with numbers, but I can't since the memory is not there, I can only do it without a number.
typedef struct {
char *path[10];
char *word[10];
int number_ocurrency[10];
int flag[10];
} shared_data_type;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int fd;
int data_size = sizeof(shared_data_type),i;
pid_t pid;
shared_data_type *shared_data;
if((fd = shm_open("/ex06_searchWord", O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR))==-1)
{
perror("Error at shared memory allocation!\n");
return 0;
}
ftruncate (fd, data_size);
shared_data = (shared_data_type *)mmap(NULL,data_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
for(i=0; i<10;i++){
shared_data->flag[i]=0;
pid=fork();
if (pid == -1)
{
printf("Error at fork!\n");
return -1;
}
if (pid == 0) { /* reader */
while(!shared_data->flag[i]);
FILE *file;
file = fopen(shared_data->path[i], "r");
if (file == NULL)
{
printf("Could not open/find the specified file.\n");
return -1;
}
int size = 0;
char readChar=NULL;
char *msg = NULL;
while((readChar = fgetc(file)) != EOF) {
msg = (char *) realloc(msg, size+1);
*(msg + size) = readChar;
size++;
}
*(msg + size) = '\0';
int count = 0;
while(msg = strstr(msg, shared_data->word[i]))
{
count++;
msg++;
}
shared_data->number_ocurrency[i]=count;
exit(0);
}
if(pid>0){
shared_data->word[i]="SCOMP";
char path[16]="files/file1.txt";
shared_data->path[i]=malloc(sizeof(path)+1);
sprintf(shared_data->path[i],"files/file%d.txt",i);
//shared_data->path[i]= "files/file.txt";
shared_data->number_ocurrency[i]=0;
shared_data->flag[i]=1;
}
}
for(i=0; i<10;i++){
wait(NULL);
}
for(i=0; i<10;i++){
printf("The word %s in the son %d appeared: %d times\n",shared_data->word[i],i,shared_data->number_ocurrency[i]);
}
if (munmap(shared_data, data_size) == -1)
{
perror("Error at unmap!\n");
return 0;
}
if(shm_unlink("/ex06_searchWord")==-1){
perror("Error at unlink!\n");
return 0;
}
return 0;
}
When you share bytes between processes, you need to make sure those bytes contain something that is meaningful and understandable to all the processes that are going to use it. Putting pointers to memory that isn't shared in shared memory makes no sense. And unless all the processes can be guaranteed to map the shared memory at the same address, even putting absolute pointers to shared memory in shared memory makes no sense.
You can divide the shared memory into "slots" if you want and get the effect of having pointers in shared memory by placing the slot number in shared memory. The slot number will have to be translated to and from an absolute address in each process, taking into account the base address of the mapping.