Probably the core of this question has been asked a lot on this site.
I'mm working with pocketsphinx and I'm trying to play music each time I request it.
When I say "MUSIC" the program executes the music, my idea is that when I say "STOP" music should stop. I'm trying to get the PID the following way. I got this idea from this question
I though using popen I will get the PID, but isn't that way when it get to pid_t pid = strtoul(line, NULL, 10); it's returning me 0.
How I can get this PID and continue with the program running at the same time?
I'm using the same template that you will find on the pocketsphinx want to see it with modifications here: http://pastebin.com/Duu2nbCA
if(strcmp(word, "MUSIC") == 0)
{
FILE *fpipe;
char *command = (char *)"aplay BobMarley.wav";
char line[256];
if ( !(fpipe = (FILE*)popen(command,"r")) )
{ // If fpipe is NULL
perror("Problems with pipe");
exit(1);
}
fgets( line, sizeof line, fpipe);
pid_t pid = strtoul(line, NULL, 10);
printf("The id is %d\n", pid);
}
You can refer the below code to find the PID of a process. Execute with "root" permission
The argument to the executable will the name of the process for which the PID has to obtained
#define TMP_FILE "/tmp/pid"
int main(int argc, char** argv)
{
FILE *fpipe;
int pid = 0;
char command[50] = "pidof -s ";
if (argc != 2) {
printf("Invalid input\n");
return -1;
}
strcat(command, argv[1]);
strcat(command, " > "TMP_FILE);
system(command);
fpipe = fopen(TMP_FILE, "r");
fscanf(fpipe, "%d", &pid);
printf("The pid is %d\n", pid);
fclose(fpipe);
return 0;
}
Based on the sizeof the process name vary the length of the command.
Implementation 2
int main( int argc, char *argv[] )
{
FILE *fp;
char path[10];
fp = popen("/sbin/pidof -s YOUR_APP", "r");
if (fp == NULL) {
printf("Failed to run command\n" );
exit(1);
}
/* Read the output a line at a time - output it. */
while (fgets(path, sizeof(path), fp) != NULL) {
printf("%s", path);
}
pclose(fp);
return 0;
}
Change YOUR_APP with your application name.
Tested with other commands.
Related
I am creating a Linux type shell program for a school project. So far I have implemented the basic Linux external commands like "ls","ps", etc., using execvp and basic pipes. As part of the project, the user can either run the program in interactive mode or batch mode. In interactive mode the user just enters a command when prompted. For batch mode, the user specifies a file in the command line where there is a list of commands to execute.
The problem I am having is in batch mode. In batch mode, if an invalid command is listed (e.g. "kdfg", for which "kdfg: command not found" while be the output), everything afterward continues, but everything afterward is executed twice. so if I have a "kghd" on one line and the next line is "ls", then the "ls" command will be executed twice. I've literally been looking at my code for hours and have tried a bunch of stuff, but to no avail.
My code is displayed below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include<sys/wait.h>
#include<unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
char* InputString();
char** GetArgs(char* com);
void Free(char** args);
char** GetCommands(char** line);
void PipedCommands(char* line);
int batch = 0; //Acts as bool for if there is a batch file given at command line
FILE* bFile; //This is just to make a quick tweek to the file if necssary to prevent any undefined behavior and keep track of where we are in the fil.
int b_fd;
int stdin_cpy;
int main(int argc, char** argv)
{
pid_t pid;
int status;
int fd;
int exitCom = 0; //acts as bool to check if an exit command was given.
char* line;
if(argc > 1) //check if batch file was given.
{
/*
if(freopen(argv[1], "r", stdin) == NULL) //I added this in case the file isn't found
{
printf("\nCould not open \"%s\". Terminated\n\n", argv[1]);
exit(1);
}
*/
//The following is to append a newline at the end of the file (if there isn't one).
//For some reaosn, there seems to be some undefined behavior if the input file isn't
//stricitly ended with a newline.
bFile = fopen(argv[1], "r+"); //open for reading and updating
if(bFile == NULL)
{
printf("\nCould not open \"%s\". Terminated\n\n", argv[1]);
exit(1);
}
fseek(bFile, -1, SEEK_END); //go to last character of file
if(fgetc(bFile) != '\n') //If last character is not a newline, append a newline to the file.
{
fprintf(bFile, "\n");
}
fclose(bFile); //close the file.
bFile = fopen(argv[1], "r"); //open file to keep track of when it ends
b_fd = open(argv[1], O_RDONLY); //open file again (with file descriptor this time) to duplicate it to stdin
stdin_cpy = dup(fileno(stdin)); //keep track of stdin file.
dup2(b_fd, 0); //duplicate to stdin so program takes input from bFile
close(b_fd);
batch = 1;
}
//int i=0; //this was used for debugging purposes
while(1)
{
printf("\n");
char** coms = GetCommands(&line);
for(int i=0; coms[i] != NULL; ++i) //loop goes through each command returned from GetCommands(...)
{
//fork and wait.
pid = fork();
wait(&status);
if(pid == 0)
{
int pipedCommand = 0;
//printf("\ncoms[%d]: %s\n", i, coms[i]);
for(int j=0; j<strlen(coms[i]); ++j)
{
if(coms[i][j] == '|')
{
pipedCommand = 1;
break;
}
}
if(pipedCommand == 1)
{
PipedCommands(coms[i]);
exit(1);
}
char** args = GetArgs(coms[i]);
//printf("\nargs[0]: %s\n", args[0]);
if(strcmp(args[0],"exit") == 0)
{
exit(5); //if exit command was given, exit status will be 5 (I just used 5 becuse I felt like it).
}
//printf("\nNo exit\n");
printf("\n");
execvp(args[0],args);
printf("%s: command not found\n", args[0]);
exit(1); //Normal exit exits with 1 or 0.
}
//Parent continues after child exits
else if(pid > 0)
{
//check exit status of child
if(WEXITSTATUS(status) == 5)
exitCom = 1; //set bool exitCom to 1 (true), indicating that the exit command was given
}
}
if(pid > 0)
{
free(line);
free(coms);
//Now that all commands in the line were executed, check exitCom and if it is 1 (exit command was given), the shell can now exit.
if(exitCom == 1)
{
printf("\n");
exit(0);
}
}
/*
if(i >= 5)
{
printf("\nFORCED EXIT\n"); //this was used for debugging purposes
exit(1);
}
++i;
*/
}
return 0;
}
char* InputString()
{
int len = 20;
char* str = (char*)malloc(sizeof(char)*len);
char* buff;
unsigned int i=0;
if(str != NULL)
{
int c = EOF;
//printf("%c", fgetc(bFile));
while( ((c = getchar()) != '\n') && (c != EOF) )
{
/*
//printf("%c", fgetc(bFile));
//fgetc(bFile);
if(feof(bFile))
{
printf("\n\nEnd of the line\n\n");
}
*/
str[i++] = (char)c;
if(i == len)
{
len = len*2;
str = (char*)realloc(str,sizeof(char)*len);
}
}
str[i] = '\0';
buff = (char*)malloc(i);
}
if(batch == 1)
{
if(fgets(buff, i, bFile) == NULL) //Once the end of file has been reached
{
dup2(stdin_cpy, 0); //revert input back to original stdin file so user can now enter commands interactively (this happens if exit command was not given)
close(stdin_cpy); //close stdin_copy
fclose(bFile); //close bFile as we have reached the end of it
batch = 0;
}
}
printf("\n");
return str;
}
//User enters a line of commands (1 or more). Commands are separated with a ';' being the delimeter.
char** GetCommands(char** line)
{
char** coms = (char**)malloc(sizeof(char*));
char delim[] = ";";
if(batch == 0)
printf("prompt> ");
fflush(stdout);
*line = InputString();
if(batch == 1)
printf("%s\n", *line);
strcat(*line, ";");
int i=0;
coms[i] = strtok(*line, delim);
while(coms[i] != NULL)
{
++i;
coms = (char**)realloc(coms, sizeof(char*) * (i+1));
coms[i] = strtok(NULL, delim);
//printf("\ni: %d\n", i);
}
return coms;
}
//A command obtained from GetCommands(...) is separated into various arguments with a space, ' ', being the delimiter.
char** GetArgs(char* com)
{
char** args = (char**)malloc(sizeof(char*));
char delim[] = " ";
//printf("\nline: %s\n", line);
int i=0;
args[i] = strtok(com, delim);
while(args[i] != NULL)
{
++i;
args = (char**)realloc(args, sizeof(char*) * (i+1));
args[i] = strtok(NULL, delim);
}
return args;
}
void PipedCommands(char* line)
{
char** coms = (char**)malloc(sizeof(char*));
int numComs;
char delim[] = "|";
int i=0;
coms[i] = strtok(line, delim);
while(coms[i] != NULL)
{
++i;
coms = (char**)realloc(coms, sizeof(char*) * (i+1));
coms[i] = strtok(NULL, delim);
}
numComs = i;
int fd[2];
pid_t pid;
int status;
int prev_p = 0;
// printf("\nnumComs: %d\n", numComs);
for(int i=0; i<numComs; ++i)
{
//printf("\ni: %d\n", i);
pipe(fd);
pid = fork();
wait(&status);
if(pid == 0)
{
//printf("\nChild\n");
if(i < numComs-1)
{
//printf("\ni < numComs-1\n");
//printf("%s", coms[i]);
//printf("coms[%d]: %s", i, coms[i]);
//printf("\nBefore dup2\n");
char** args = GetArgs(coms[i]);
//printf("\nexecvp in if\n");
if(prev_p != 0)
{
dup2(prev_p, 0);
close(prev_p);
}
dup2(fd[1], 1);
close(fd[1]);
execvp(args[0],args);
printf("%s: command not found\n", args[0]);
exit(3);
}
else
{
//printf("\nelse\n");
//printf("coms[%d]: %s", i, coms[i]);
//printf("\nBefore dup2 in else\n");
if(prev_p != 0)
{
dup2(prev_p, 0);
close(prev_p);
}
//close(fd[0]);
close(fd[1]);
char** args = GetArgs(coms[i]);
printf("\n");
execvp(args[0],args);
printf("%s: command not found\n", args[0]);
exit(3);
}
}
close(prev_p);
close(fd[1]);
prev_p = fd[0];
if(WEXITSTATUS(status) == 3)
{
close(fd[0]);
close(prev_p);
close(fd[1]);
return;
}
}
close(fd[0]);
close(prev_p);
close(fd[1]);
}
You can probably ignore the PipedCommands(...) function as I do not think the problem lies there.
Below is a simple batch file:
kldfg
whoami
Below is the output using the above batch file
kldfg
kldfg: command not found
whoami
jco0100
whoami
jco0100
The whoami command should only execute once, but it appears to execute twice. After that, the program reverts to interactive mode as it should and everything runs fine from there. Does anyone have any idea why this is happening. This only happens when an unknown command is entered. If all commands in the batch file are valid, nothing is outputted twice. It's only for batch files that have an unknown command that all commands after the unknown one are outputted twice.
Here's another example:
Batch file:
date
kldfg
whoami; ls | wc -l
date | wc -c
Output:
date
Tue Apr 13 19:43:19 CDT 2021
kldfg
kldfg: command not found
whoami; ls | wc -l
jco0100
34
date | wc -c
29
whoami; ls | wc -l
jco0100
34
date | wc -c
29
I got it working by disconnecting stdin on the child process before running the command:
...
freopen("/dev/null", "r", stdin); // disconnect
execcvp(args[0], args);
...
From this link: If I fork() and then do an execv(), who owns the console?
I had a crack at debugging it. To get you started on that road:
Compile your C program with debugging symbols:
$ gcc --debug your-program.c
Now debug your C program:
$ gdb a.out
This start a gdb interactive shell.
In gdb itself:
(gdb) list
(gdb) set follow-fork-mode parent
(gdb) breakpoint 69
list your code
tell the debugger to follow the parent when fork()
set a breakpoint at line 69
Run the program:
(gdb) run batch.txt
It will pause at line 69. Execute next line:
(gdb) next
Print a variable:
(gdb) print *coms
Continue running:
(gdb) continue
I leave the rest for you to explore.
I'm still not sure what's wrong with it. Something strange happens in InputString() after your fork fails with an unknown command. InputString() begins returning duplicates from getchar().
I didn't even know you could do that with stdin. Maybe just read from the file in a normal fashion, rather than clobbering stdin, and see if the problem goes away.
Don't write linux code much, I am trying to do more of that.
I took the fork and wait commands out so I could build it in mingw64 (because they arent supported in windows builds) and can't seem to reproduce the issue.
So I think the issue is in the multi-threading setup you have going there.
The "pid" variable is shared between every fork. which means when the fork command is called "pid" is set to whatever the last fork in the loop returned.
It looks like you are using an if statement checking the "pid" variable to see if this thread can execute the command. But wouldn't the main thread keep running right through that?
I don't know what fork() returns but "pid" is uninitialized, don't know if that matters.
Maybe this helps?
I am tryinIg to send log-events from different threads through a FIFO, read the events and write them to a file.
First I forked my main(). In the Child I want to run the FIFO reader and in the Parent i start my threads (these works fine).
#define FIFO "logFifo"
#define MAX_BUF 1024
int main(int argc, char *argv[]){
pid_t pid;
pid=fork();
if(pid<0){
printf("error with fork() \n");exit(1);
}
if(pid == (pid_t) 0){
//FIFO lezen van log_file
int err;
char buf[MAX_BUF];
char * str_result;
FILE *log;
err = mkfifo(FIFO, 0666);
CHECK_MKFIFO(err);
log = fopen(FIFO, "r");
FILE_OPEN_ERROR(log);
while(1){
//presult = pthread_mutex_lock( &log_mutex);
//pthread_err_handler( presult, "pthread_mutex_lock", __FILE__);
str_result = fgets(buf, MAX_BUF,log);
if(str_result != NULL){
FILE *fp;
fp = fopen("gateway.log","w");
fprintf(fp,"%s \n",buf);
//fclose(fp);
}
//presult = pthread_mutex_unlock( &log_mutex);
//pthread_err_handler( presult, "pthread_mutex_unlock", __FILE__ );
sequence++;
//usleep(1);
}
//unlink(FIFO);
}
else{
//Parent code}
return 0;}
I made a function that will send the log-events:
void send_log(char * message){
FILE *log;
int err;
err = mkfifo(FIFO, 0666);
CHECK_MKFIFO(err);
log = fopen(FIFO, "w");
FILE_OPEN_ERROR(log);
char *msg;
asprintf(&msg, "%d %lu %s", sequence,(unsigned long)time(NULL),message);
printf("log message: %s \n",msg);
if( fputs(msg,log) == EOF){
fprintf(stderr, "error writing data to fifo \n");
exit (EXIT_FAILURE);
}
FFLUSH_ERROR(fflush(log));
free(msg);
sequence++;
/*err = fclose(log);
FILE_CLOSE_ERROR(err);*/
}
I know I put some things in commment, but I tried it also with those in code. Can anyone help me out?
Thank you!
EDIT : solution was to use "aw" instead of "w" and open my gateway.log in the while loop
I am trying to open a text file and have both a child and parent process read from the file one line at a time and output their PID followed by the line that was read. Except no matter what I seem to do I keep getting a seg fault and can't even locate where my problem is.
int main(int argc, char *argv[]){
if(!argv[1]){
return 1;
}
int var1 = strtol(argv[1], NULL, 10);
FILE *fp;
fp = fopen("prog2Boutput.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Can't open input file in.list!\n");
exit(1);
}
int childP = fork();
if(childP<0){
fprintf(stderr, "fork failed\n");
exit(1);
}
else if(childP==0){
char* line = fgets(line,150, fp);
printf("child: (pid:%d) (line:%s)\n", (int)getpid(), line);
}
else{
char* line = fgets(line,150,fp);
printf("parent: (pid:%d) (line:%s)\n", (int)getpid(), line);
}
fclose(fp);
return 0;
}
You might have two errors:
You're using strtol() without checking that there's an argv[1]. You should put in a guard.
You aren't using fgets() correctly. The first argument must be a buffer that you allocate yourself, as in:
char line[200];
char *cp = fgets(line, 150, fp);
After that cp will be either 0 (when fgets() couldn't read anything) or it will point to the first character of line.
(Last, as point of style, main() should return an int, not void. Some modern compilers won't accept your code.)
So I am reading commands from a file that has a line of commands, each separated by a delimiter which is a semi colon. I got these commands into an array and I am basically executing them one by one. Everything works fine until I have a command that has an option and execvp fails and I don't know how to fix this.
Here is my code:
int main(int argc, char *argv[])
{
char delim[] = ";"; // the semicolon is the commands separator
FILE* batchFile;
char oneLine[512];
batchFile = fopen("myfile.txt", "r");
int numOfCommands = 0;
char *commands[100];
char *oneCommand;
pid_t childPid;
int child_status;
if(batchFile == NULL)
{
perror("Error opening file ... exiting !");
exit(1);
}
if(fgets(oneLine,512,batchFile) != NULL)
{
//puts(mystring);
fclose(batchFile);
}
printf("The command is: %s \n", oneLine);
oneCommand = strtok(oneLine,delim);
commands[numOfCommands++] = strdup(oneCommand);
while((oneCommand=strtok(NULL, delim))!=NULL)
{
commands[numOfCommands++] = strdup(oneCommand);
}
commands[numOfCommands] = NULL;
for(int i = 0;i < numOfCommands;i++)
{
printf("The command is: %s \n",commands[i]);
}
for(int i =0;i < numOfCommands;i++)
{
childPid = fork();
if(childPid == 0)
{
execvp(commands[i], argv);
perror("exec failure");
exit(1);
}
else
{
wait(&child_status);
}
}
return 1;
}
and some commands like exit, cd will not work, I guess maybe because they are not in /bin ??
and how could this be fixed ?
My file has the following line
ls;date;cal;pwd;cd;ls -l;
and when I run my program it outputs the following.
If you look at the output, it fails for cd. This is expected because cd is a shell built-in, not a command. For cd, you have to use chdir(2) instead of execv().
ls -l fails because there's no such command. You need to split the command again before passing them to execvp().
Basically, the command you pass has to be in the form:
char *cmd[] = {"ls", "-l", 0};
execvp(cmd[0], cmd);
I'm trying to implement a simple shell in C and my program is causing an infinite loop and creating a ton of new processes/printing Myshell> before the user has a chance to enter in any commands. I can't seem to find a way to prevent this and if anyone could help that would be great! (Didn't paste the #include headers at top
int main(int argc, char *argv[]){
char buffer[512];
int *status;
size_t nargs;
pid_t pid;
char delim[] = "\n";
while(1){
pid = fork();
if(pid){
printf("Myshell> ");
fgets(buffer, 512, stdin);
//parse(buffer, argv);
argv[0] = strtok(buffer, delim);
for(argc=1; argv[argc-1]; argc++){
argv[argc] = strtok(NULL, delim);
}
if(!strcmp(argv[0], "exit"))
exit(0);
printf("Waiting for child (%d)\n", pid);
pid = wait(status);
printf("Child (%d) finished\n", pid);
exit(0);
}else{
if(execvp(argv[0], argv)){
printf("error");
exit(1);
}else{
exit(0);
}
}
}
return 0;
}
Because you are reading command into buffer using fgets but not from command line argument So argv[argc] for argc > 1 is wrong - undefined behaviour.
When you don't pass any extra command line argument then argv[0] is your program name and argv[1] is NULL. indexing to argv[] for value more then 1 causes array out of index problem.
Instead of declare argv[] and argc as main function parameter declare within main as formal variable something like:
int argc;
char* argv[MAX]; // max number of argument can be pass to your shell
One more rectification in your code, change:
int *status;
as
int status;
and accordingly correct
pid = wait(status);
as
pid = wait(&status);