We were asked to prompt the user to enter phrases, and continue asking them until they get the correct phrase needed, for 30 seconds. Here's what I've come up with:
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void childprocess(void)
{
int start = 30;
do
{
start--;
sleep(1);
} while (start >= 0);
printf("Time ran out!\n");
exit(EXIT_SUCCESS);
}
int main(void)
{
pid_tiChildID;/* Holds PID of current child */
char word[100] = "cat";
char input[100];
int length;
iChildID = fork();
if (0 > iChildID)
{
perror(NULL);
return 1;
}
else if (0 == iChildID)
{
childprocess();
return 0;
}
/* Parent process */
while (1)
{
fgets(input, sizeof(input), stdin);
length = strlen(input);
if (input[length - 1] == '\n')
{
--length;
input[length] = '\0';
}
if (strcmp(word, input) == 0)
break;
printf("Try again\n");
}
kill(iChildID, SIGUSR1);/* terminate repeating message */
printf("Finally!\n");
return 0;
}
The problem: after 30 seconds, it prints "Time runs out" but won't terminate. How do I terminate the program after 30 seconds? Any help?
Here, you are using fork which creates two separate processes with two different PIDs. You are killing child process but parent is still running so program just dont quit.
You could have also used pthread instead of fork with remains in same single process but what ever you are trying to achieve is simple with alarm function. You dont have to manage any other process. Just use alarm.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void ALARMhandler(int sig)
{
printf("Time ran out!\n");
exit(EXIT_SUCCESS);
}
int main(void)
{
char word[100] = "cat";
char input[100];
size_t length;
signal(SIGALRM, ALARMhandler);
alarm(30);
while(1) {
fgets(input, sizeof(input),stdin);
length = strlen(input);
if(input[length-1] == '\n') {
--length;
input[length] = '\0';
}
if (strcmp(word,input) == 0)
break;
printf("Try again\n");
}
/* terminate repeating message */
printf("Finally!\n");
return 0;
}
Hope it helps !!
Related
Why this code may generate Runtime Error in ejudge? This program counts the number of words from stdin input. Words can be separated by any amount of ' ' and '\n'.
It seems like fork() can cause a problem but I am not sure why I don't get the same error on my computer.
ejudge uses gcc - Plain C, 64 bit, using -std=c11 or -std=gnu11
The task:
On the standard input stream a text string is given which consists of
words (a sequence of non-space characters), between which there can be
any number of whitespace characters, including line feeds.
You need to calculate the number of words if you know there are not
more than 255, and output this value to the standard output stream.
Use creating new processes so that each process reads not more than
one word, e.g. using scanf("%s", ...).
You can only output the result from the process which was started
first (i.e. from the original program).
The resulting program must return with return code 0.
The size of each word does not exceed 4096 bytes.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#define DBG(args...) fprintf(stderr, args)
//#define DBG(args...)
int main(int argc, char* argv[])
{
int status;
pid_t pid;
pid_t first_child;
for (int i = 0; i < 256; ++i) {
pid = fork();
if (pid == 0) { // child continue reading
char str[4097];
if (scanf("%s", str) != EOF)
continue;
exit(1);
} else {
if (i == 1) {
first_child = pid;
}
if (wait(&status) == first_child) {
break;
} else {
exit(WEXITSTATUS(status) + 1);
}
}
}
fprintf(stdout, "%i\n", WEXITSTATUS(status));
fflush(stdout);
fclose(stdout);
return 0;
}
Rewrote the algorithm and it worked!
In the first version, many unnecessary forks were made. For example, if 6 were intended it created 12.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
// #define DBG(args...) fprintf(stderr, args)
#define DBG(args...)
int main(int argc, char* argv[])
{
int status;
pid_t first_pid;
pid_t pid = fork();
if (pid != 0) {
wait(&status);
printf("%i\n", WEXITSTATUS(status));
return 0;
}
for (int i = 0; i < 256; ++i) {
char str[4097];
if (scanf("%s", str) == EOF) {
DBG("PID %i\n", pid);
exit(0);
}
pid = fork();
if (pid != 0)
break;
}
DBG("PID %i waiting\n", pid);
wait(&status);
exit(WEXITSTATUS(status) + 1);
}
I have two programs: Program "Vanilla", and program "verB".
My instructions are that the main process will deal with I\O from the user, and the child will call execve() and run the "Vanilla" process. To accomplish this, I have to use dup2() to replace stdin\stdout on both pipes. (The Vanilla program should use fgets() to read from the pipe).
Inside the "Vanilla" program I read two strings from the user until ctrl+D is pressed, Vanilla calls "xorMethod()" which is doing something (not relevant what) and returns a result.
When I run the "verB" program on Linux(), I only get the "Please insert first, the second string" and then nothing happens and the program stops running.
I want that the parent will continue getting two strings until ctrl+D is pressed, and print the result that he got from his child on the screen.
Vanilla.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Vanila.h"
#include "xor.h"
#define MAXLEN 80
int main()
{
char s1[MAXLEN + 1];
char s2[MAXLEN + 1];
while (!feof(stdin))
{
if (readString(s1) == -1)
break;
if (readString(s2) == -1)
break;
fflush(stdin);
int res = xorMethod(s1, s2);
printf("%s xor %s = %d", s1, s2, res);
}
return 1;
}
int readString(char * string)
{
if ((fgets(string, MAXLEN + 1, stdin) < 0 || feof(stdin)))
return -1;
string[strcspn(string, "\n")] = 0;
return 1;
}
verB.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Vanila.h"
#define MAXLEN 80
int readStr(char * string);
int main()
{
int pipeToChild[2];
int pipeToParent[2];
char * argv[] = { "./Vanilla",NULL };
if (pipe(pipeToChild) == -1)
return -1;
if (pipe(pipeToParent) == -1)
return -1;
pid_t pid = fork();
if (pid == -1)
return -1;
if (pid == 0) //CHILD proccess
{
close(pipeToChild[0]);
close(pipeToParent[1]);
dup2(pipeToChild[0], fileno(stdin));
dup2(pipeToParent[1], fileno(stdout));
execve(argv[0], argv, NULL);
}
else
{
char string1[MAXLEN + 1];
char string2[MAXLEN + 1];
char result[MAXLEN + 1];
close(pipeToChild[0]);
close(pipeToParent[1]);
while (!feof(stdin))
{
printf("Please insert first string : ");
if (readStr(string1) == -1)
return -1;
printf("Please insert second string : ");
if (readStr(string2) == -1)
return -1;
write(pipeToChild[1], string1, strlen(string1));
write(pipeToChild[1], string2, strlen(string2));
read(pipeToParent[0], &result, MAXLEN);
printf("%s\n", result);
}
wait(NULL);
}
return 1;
}
int readStr(char * string)
{
if ((fgets(string, MAXLEN + 1, stdin) < 0 || feof(stdin)))
return -1;
string[strcspn(string, "\n")] = 0;
return 1;
}
You close the wrong ends of the pipes in the child process. You close the read end of pipeToChild and then dup2 the standard input stream, so you child program will have a closed standard input stream. You should close the write end of pipeToChild and the read end of pipeToParent in the child process, and the other way around in the main process:
if (pid == 0) //CHILD proccess
{
close(pipeToChild[1]);
close(pipeToParent[0]);
/* ... */
else //PARENT
{
close(pipeToChild[0]);
close(pipeToParent[1]);
I have been writing a c program which pipes data from one file to the next however it has been infinite looping.
What i have discovered so far. The infinite loop is caused on file c1.c where perror (or stderr) skips the scanf. If scanf does work. The program infinite loops further down the track printing out the perror even though it is past that section!
My code is below
controller.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
int main(int ac, char**av)
{
int pipez[2];
int piped[2];
int status;
pid_t pid;
if (pipe (pipez) == -1){
perror("could not make pipe");
return 1;
}
if ((pid = fork()) == -1){
perror("fork");
return 1;
}
if(pid == 0){
close(pipez[1]);
dup2(pipez[0],0);
close(pipez[0]);
execvp("./c1",av);
perror("demo);
_exit(1);
}
else{
close(pipez[0]);
dup2(pipez[1],1);
close(pipez[1]);
execvp("./c2",av);
perror("demo");
exit(1);
}
waitpid(pid,&status,0);
if(WIFEXITED(status)){
printf("[%d] TERMINATED (Status: %d)\n", pid, WEXITSTATUS(status));
}
if(pipe (piped) == -1){
perror("could not make pipe");
return 1;
}
if((pid = fork()) == -1){
perror("fork");
return 1;
}
if (pid == 0){
close(piped[1]);
dup2(piped[0],0);
close(piped[0]);
execvp("./c2", av);
perror("demo");
_exit(1);
}
else{
close(piped[0]);
dup2(piped(piped[1],1);
close(piped[1]);
execvp("./c3",av);
perror("demo");
exit(1);
}
waitpid(pid, &status, 0);
if (WIFEXITED(status)){
printf("[%d] TERMINATED (Status: %d)\n", pid, WEXITSTATUS(status));
}
return 0;
}
c1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
//This program is causing infinite loop, tried fflush and fgets and scanf
// It will run independently but will loop via the pipe
int main(int a, char**av){
char store[BUFSIZE];
memset(store, '\0', sizeof(store));
while(strcmp(store, "exit!") != 0){
perror("Please enter next line of input (type 'exit!' to stop) \n"); //This repeats itself infinitely
fgets(store, BUFSIZE, stdin);
printf("%s",store); // This also repeats itself dependant on where i put
//fflush or another printf. Repeated outputs occur in blocks
}
return 0;
}
c2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
char *strlwr(char *str);
int main(int ac, char**av){
char store[BUFSIZE];
while(strcmp(store, "exit!") != 0){
scanf("%s", store);
printf("%s", strlwr(store));
}
return 0;
}
char *strlwr(char *str){
unsigned char *p = (unsigned char *)str;
while(*p){
*p = tolower((unsigned char)*p);
p++;
}
return str;
}
c3.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
int main(int ac, char**av){
char store[BUFSIZE];
int n = 0;
while(strcmp(store, "exit!") != 0{
scanf("%s",store);
printf("Line %d: %s\n",n,store);
n++;
}
return 0;
}
fgets leaves '\n' char in the store buffer so, easiest fix is to add '\n' to the compared string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
//This program is causing infinite loop, tried fflush and fgets and scanf
// It will run independently but will loop via the pipe
int main(int a, char**av)
{
char store[BUFSIZE];
memset(store, '\0', sizeof(store));
while (strcmp(store, "exit!\n") != 0)
{
perror("Please enter next line of input (type 'exit!' to stop) \n"); //This repeats itself infinitely
fgets(store, BUFSIZE, stdin);
printf("%s", store); // This also repeats itself dependant on where i put
// fflush or another printf. Repeated outputs occur in blocks
}
return 0;
}
Moreover, as you can see, init value '/0' is not acceptable; it must be '\0'. Your compiler is probably telling you something like
test.c: In function ‘main’:
test.c:242:16: warning: multi-character character constant [-Wmultichar]
memset(store, '/0', sizeof(store));
^
BTW memset, in this case is useless because fgets does all the job, but anyway, if you want you buffer set to nul, just declare:
char store[BUFSIZE] = {0};
Best solution using fgets is (obviously) always manage the newline left:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 256
//This program is causing infinite loop, tried fflush and fgets and scanf
// It will run independently but will loop via the pipe
int main(void)
{
char store[BUFSIZE] = {0};
char *pos;
while (strcmp(store, "exit!") != 0)
{
perror("Please enter next line of input (type 'exit!' to stop) \n"); //This repeats itself infinitely
fgets(store, BUFSIZE, stdin);
if ((pos = strchr(store, '\n')) != NULL)
*pos = '\0';
else
fprintf(stderr, "String too long!\n");
printf("%s", store); // This also repeats itself dependant on where i put
// fflush or another printf. Repeated outputs occur in blocks
}
return 0;
}
This is bugging me for days.
The problem is my not so good understanding of pointers and addresses in c so i hope someone will be able to help me out.
I need to pass some strings as input parameters and create as much producer processes + one consumer process.
Producers should take the string apart and send each letter as message to queue. At the end it should send NULL("").
The consumer should wait for messages and print them out.
The whole code and output is below. By looking at the output i'd say that the problem is somewhere in the producer. To be more precise it is in the first line of te for loop but i can not get it right.
manager.c - This is the main program that operates processes
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/msg.h>
int main( int argc, char *argv[], char *envp[] ) {
printf("Starting %d processes \n", argc);
putenv("MSG_KEY=12345");
for (int i = 1; i < argc; i++) {
printf("argv[%d] = %s \n", i, argv[i]);
pid_t producer = fork();
if (producer == 0) {
printf("producer pid - %d\n", getpid());
execl("./producer", "producer", argv[i], NULL);
}
}
pid_t consumer = fork();
if (consumer == 0) {
printf("consumer pid - %d\n", getpid());
execl("./consumer", "consumer", NULL);
exit(0);
} else {
printf("manager pid - %d\n", getpid());
wait(NULL);
}
int status;
while(waitpid(consumer, &status, 0) == -1);
printf("DONE consumer\n");
printf("DONE manager\n");
return 0;
}
producer.c
/*
** writes to message queue
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msgbuf {
long mtype;
char mtext[1];
};
int main( int argc, char *argv[], char *envp[] ) {
struct my_msgbuf buf;
int msqid;
key_t key = atoi(getenv("MSG_KEY"));
if ((msqid = msgget(key, 0600 | IPC_CREAT)) == -1) {
perror("msgget");
exit(1);
}
buf.mtype = getpid();
// I believe the error is in this for loop or to be more precise in the first line of the for loop.
// takes the first argument and sends characters in separate messages
for (int i = 0; i < strlen(argv[1]); ++i) {
char c = argv[1][i];
strcpy(buf.mtext, &c);
printf ("Sending -%s-\n", buf.mtext);
if (msgsnd(msqid, (struct msgbuf *)&buf, strlen(buf.mtext)+1, 0) == -1)
perror("msgsnd");
}
// send NULL at the end
memcpy(buf.mtext, "", strlen("")+1);
if (msgsnd(msqid, (struct msgbuf *)&buf, strlen("")+1, 0) == -1)
perror("msgsnd");
return 0;
}
consumer.c
/*
** reads from message queue
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msgbuf {
long mtype;
char mtext[1];
};
int main( int argc, char *argv[], char *envp[] ) {
struct my_msgbuf buf;
int msqid;
key_t key = atoi(getenv("MSG_KEY"));
if ((msqid = msgget(key, 0600 | IPC_CREAT)) == -1) {
perror("msgget");
exit(1);
}
int flag = 0;
int wait_counter = 0;
while (wait_counter < 10) {
msgrcv(msqid, (struct msgbuf *)&buf, sizeof(buf)-sizeof(long), 0, flag);
if (errno == ENOMSG){
wait_counter++;
printf ("Sleaping for one second...zzzZZZzzz...%d\n", wait_counter);
usleep(1000 * 1000);
} else {
printf("Received:\n\ttype: -%ld- \n\tchar: -%s- \n", buf.mtype, buf.mtext);
int compare = strcmp(buf.mtext, "");
if(compare == 0){
printf("NULL received\n");
flag = IPC_NOWAIT;
} else {
flag = 0;
}
wait_counter = 0;
}
errno = 0;
}
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
} else {
printf("Message queue removed\n");
}
return 0;
}
Output - i have to give you the screenshot here because c/p deletes the problem and everything looks ok
Any help will be much appreciated! Thank you!
Error when used as suggested in the #sergeya answer below
*buf.mtext = c;
Your problem (one of them, at least) is here:
char c = argv[1][i];
strcpy(buf.mtext, &c);
strcpy() will try to copy as many characters as there are until nul-terminator '\0' is encountered, starting from c. You need to copy one character exactly, so you just need
*buf.mtext = c;
As i said, the problem was in the producer inside the for loop. I will put the change here. Hope it helps anyone with the similar problem.
#SergeyA gave me excellent clue where the problem is so i switched from "strcpy" to "memcpy" and i have copied just the first character and not the nul-terminator.
Also i have changed the "strlen" to "sizeof" and removed the +1.
Producer.c
...
for (int i = 0; i < strlen(argv[1]); ++i) {
char c = argv[1][i];
memcpy(buf.mtext, &c, sizeof(&c)+1);
printf ("Sending -%c-\n", buf.mtext);
if (msgsnd(msqid, (struct msgbuf *)&buf, sizeof(buf.mtext), 0) == -1)
perror("msgsnd");
}
...
i am have a program named "test" that executes another program called "hello". After receiving the name of the desired program, my program seems to wait for more input in order to display the "hello" program code. A snippet of example code
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> /* for fork */
#include <sys/wait.h> /* for wait */
int main() {
char *temp[] = {NULL,NULL,NULL,NULL};
char buf[BUFSIZ];
char s[256];
while(1) {
printf("type r at next input\n");
fgets(buf, sizeof(buf), stdin);
strtok(buf, "\n");
if((strcmp(buf,"r")) == 0) { //if r typed
printf("run what file : ");
scanf("%s ",s);
pid_t i = fork();
if (i == 0) //we are in child process
{
execv(s,temp);
_exit(1);
}
if (i != 0) { //parent
wait(NULL);
}
}
else
exit(0);
}
}
the "hello" program is...
#include<stdio.h>
main(int argc, char* argv[])
{
printf("\nHello World\n");
}
and an example run from the shell on linux is...* means my input, // are comments
issac#issac-ThinkPad-T440s ~$ ./a.out
type r at next input
*r*
run what file : *hello*
*r* //i cant proceed unless i type a character so i input *r*?
Hello World //after ?additional? input it finally displays the "hello" program code
type r at next input //loops back but program skips my input?
run what file ex ? hello :
i realize this may be a simple mistake but i cant figure out whats wrong with this. i realize the last part with the skipped input is probably due to the input buffer having the newline in there but more confusing is why after executing "hello" my program waits for more input to display result.
To answer your first question: drop a trailing whitespace in scanf() so it looks like this: scanf("%s",s);.
To answer your second question: your problem is that you mix scanf() and fgets(). A newline is not consumed be scanf() and is passed as a new input to the next (non-first) fgets. The easiest solution is to use fgets in both places:
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> /* for fork */
#include <sys/wait.h> /* for wait */
#include <string.h>
int main() {
char *temp[] = {NULL,NULL,NULL,NULL};
char buf[BUFSIZ];
char s[256];
while(1) {
printf("type r at next input\n");
fgets(buf, sizeof(buf), stdin);
strtok(buf, "\n");
if((strcmp(buf,"r")) == 0) { //if r typed
printf("run what file : ");
fgets(s, sizeof(s), stdin);
strtok(s, "\n");
pid_t i = fork();
if (i == 0) //we are in child process
{
execv(s,temp);
_exit(1);
}
if (i != 0) { //parent
wait(NULL);
}
}
else
exit(0);
}
}