Creating a UNIX Shell, confused about fork and child pids? - c

Here is my code for the evaluate function, which is called in the main.
void eval(char *cmdline)
{
char *argv[MAXARGS]; /* argv for execve() */
int bg; /* should the job run in bg or fg? */
pid_t pid; /* process id */
bg = parseline(cmdline, argv);
struct job_t tempJob;
if (builtin_cmd(argv) == 0)
{
if ((pid = fork()) == 0)
{ /* child runs user job */
if (execve(argv[0], argv, environ) < 0)
{
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
if (!bg)
{ /* parent waits for fg job to terminate */
int status;
if (waitpid(pid, &status, 0) < 0)
unix_error("waitfg: waitpid error");
}
else /* otherwise, don’t wait for bg job */
{
printf("%d %s", pid, cmdline);
}
}
return;
}
Now when I run a background job, I expect that the pid of the job get printed twice twice, once in parent and once for child.I just need to understand what is going on in the code. It works the way it is supposed to but why?

Both the parent and child return from fork, but the condition == 0 is true only for the child:
if ((pid = fork()) == 0)
Thus only the child runs this:
if (execve(argv[0], argv, environ) < 0)
execve replaces the current process, and thus does not return if there is no error, i.e., the child's execution of this program ends either here or at the exit(0); that follows from the error condition.
The rest of the function is run only by the parent in all cases.

Related

execvp in C not going through ar

I'm trying to use exec to execute a list of commands given as arguments.
Example input when In run the program would be ./assn2 ls date.
When I do this only the first command is executed.
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
int args = argc-1;
pid_t childpid = fork();
// error
if (childpid < 0)
{
perror("fork() error");
exit(-1);
}
// parent process
if (childpid != 0)
{
printf("Parent Process started, now waiting for ID: %d\n", childpid);
wait(NULL);
printf("Parent Process resumeed. Child exit code 0. Now terminating\n");
exit(0);
}
// child process
if (args > 0)
{
printf("Child process has begun. %d argument/s provided\n", args);
int i;
for (i = 1; i <= argc; i++)
{
execlp(argv[i], argv[i], NULL);
}
execvp(argv[1], argv);
}
else
{
printf("No arguments provided, terminating child\n");
}
return 0;
}
Once the first child process execs (and succeeds), the for loop no longer continues because the an execlp would just replace the current process image with the command being exec'ed.
What you want to do is to loop over the command line arguments in the parent process and exec once for each of the command. Something like is probably what you're after:
for(int i = 1; i < argc; i++) {
pid_t pid = fork();
if (pid == 0) {
execlp(argv[i] ,argv[i], (char*)0);
perror("exec");
} else if (pid > 0) {
wait(NULL);
} else {
perror("fork");
exit(1);
}
}
What are you trying to achieve with the sequential calls to execlp() and execvp()? These functions are not meant to return. I think you should read the ref:
The exec() family of functions replaces the current process image with a new process image. [..] The exec() functions only return if an error has occurred.
As a result you cannot execute them one after another in the same process.
Read about fork():
fork() creates a new process by duplicating the calling process.
Moreover, here:
for(i = 1; i <= argc; i++)
you go out of bounds, since argv starts indexing from 0, and ends at argc - 1.
Chnage it to:
for(i = 1; i < argc; i++)

Can't kill PID started with spawn

Some days ago, I asked here how to start a program using C without using fork(). This solution is working fine.....except that I can't kill this child process!
My program (in this case, 'rbfeeder') has some threads....and when I send kill signal, only threads are killed (I think). What am I doing wrong?
This is the code used to 'start' rbfeeder:
/*
* Start dump1090, if not running
*/
void startDump(void) {
if (p_dump != 0) {
log_level(8, "Looks like dump is already running.\n");
return;
}
pid_t ret = run_cmd("/home/jmaurin/dev/client/rbfeeder");
if (ret != 0) {
log_level(8, "Ok, started! Pid is: %i\n", ret);
p_dump = ret;
sendStats();
} else {
log_level(8, "Error starting dump1090\n");
p_dump = 0;
sendStats();
}
return;
}
And this is the code to 'stop':
void stopDump(void) {
if (checkDumpRunning()) {
log_level(3, "Dump is running, let's try to kill.\n");
if (kill(p_dump, SIGKILL) == 0) {
log_level(3,"Succesfull kill dump!\n");
sendStats();
return;
} else {
log_level(3,"Error killing dump.\n");
return;
}
} else {
log_level(3, "Dump is not running.\n");
}
return;
}
and run_cmd function:
pid_t run_cmd(char *cmd) {
pid_t pid, ret;
char *argv[] = {"sh", "-c", cmd, NULL};
int status, s;
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_t *file_actionsp;
s = posix_spawn_file_actions_init(&file_actions);
if (s != 0)
return 0;
//STDERR_FILENO
s = posix_spawn_file_actions_addclose(&file_actions,STDERR_FILENO);
if (s != 0)
return 0;
file_actionsp = &file_actions;
//printf("Run command: %s\n", cmd);
status = posix_spawn(&pid, "/bin/sh", file_actionsp, NULL, argv, environ);
if (status == 0) {
log_level(8, "Child pid: %i\n", pid);
ret = pid;
/*
if (waitpid(pid, &status, 0) != -1) {
printf("Child exited with status %i\n", status);
} else {
perror("waitpid");
}
*/
} else {
// printf("posix_spawn: %s\n", strerror(status));
ret = 0;
}
return ret;
//printf("End of run\n");
}
p_dump is a global variable to hold PID.
This image is when my 'client' receive the command (over ethernet) to start external program:
Then, an HTOP on the same machine....see that PID are the same, which means that my variable is correct:
Then, I've sent an 'stop' command and my client executed 'stopDump', but one process still running (the other threads from same program are 'killed'):
The external program doesn't 'spawn/fork' itself, but it does have threads.
Your kill is completing successfully, because the process is becoming a zombie, (The Z in the status column in HTOP). A zombie is a process that still has metadata in the kernel, but is not actually running. To get rid of the zombie, the parent has to wait on the process. As your process is the parent, adding a call to waitpid(p_dump) after the kill succeeds should handle this.
The answer by user1937198 worked fine, but I found another way that doesn't need to call any function, like 'waitpid'.
struct sigaction sigchld_action = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDWAIT
};
sigaction(SIGCHLD, &sigchld_action, NULL);
At least in my case, it's preventing zombie proccess after kill. It's works fine.

double fork causes socket in original parent to break

I do a double fork (fork, then the child forks again, I wait for the child, the grandchild is handled by init) in a process and after the child has closed I try to read from a socket that I had open in the parent previous to the fork. The read fails every time I run the program.
The message is being sent using sendto() which returns an error code of ENOENT.
Should this be working or am I [10]?
Here is the code in the function that forks:
uint8_t
upgrade(
char *server,
char *file)
{
pid_t pid1, pid2;
int status;
if ((pid1 = fork()) < 0)
{
/* Fork error */
log("FAILED: First fork() failed");
}
else if (pid1 == 0)
{
/* First child */
if ((pid2 = fork()) < 0)
{
/* Fork error */
log("FAILED: Second fork() failed");
exit(0);
}
else if (pid2 == 0)
{
/*
execl("/usr/sbin/system_upgrade",
"system_upgrade", NULL);
*/
exit(0);
}
else
{
/* Second parent ie. First child
* Note: Exit cleanly so that second child
* gets reparented to init, and we avoid a zombie process */
exit(0);
}
}
else
{
/* First parent, wait for first child to exit */
if (waitpid (pid1, &status, 0) != pid1)
{
log("FAILED: waitpid() failed");
}
} /* FORK */
return OK;
}
edit: removed extra bracket, fixed indentation, marked it as code again

How to pass signals to child process

Title may be a little confusing, so let me explain. I am trying to write a simple shell to practice my programming. I have got the get a command, fork, exec loop working. However, when I press CTRL-C while child process is still executing, my shell terminates, instead of child process (but child process would keep running). Here is the main function:
int main()
{
dynarray *args; /* pointer to a dynamic array */
int bytes_read;
size_t nbytes = 0;
char *command;
pid_t pid;
printf("Enter command: ");
while ((bytes_read = getline(&command, &nbytes, stdin)) != -1) {
if (bytes_read == -1) {
perror("getline");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) { /* child process */
args = newdynarray();
char *arg = strtok(command, " \n");
while (arg != NULL) {
addstring(args, arg);
arg = strtok(NULL, " \n");
}
if (args->nval == 0) {
freedynarray(args);
continue;
}
addstring(args, NULL);
char *fullpath = find_executable(args->strings[0]);
if (fullpath == NULL) {
fprintf(stderr, "Couldn't find executable: %s\n", command);
exit(EXIT_FAILURE);
}
if (execv(fullpath, args->strings) == -1) {
perror("execv");
exit(EXIT_FAILURE);
}
} else {
int status;
waitpid(pid, &status, 0);
}
printf("Enter command: ");
}
return 0;
}
I didn't include other parts, because I don't think they are relevant. How can I make my child process catch all the input from stdin until it terminates?
You can register a signal handler for SIGINT in your parent process, and therein use kill(2) to send a signal to the child process, whose PID you should store somewhere.
How can I make my child process catch all the input from stdin until it terminates? Signals generated from stdin keys (such as control C) will be sent to the last process to use stdin, so there's nothing you can do unless you can force your child to use the path.
Instead, you need to create a signal handler in your shell process to catch SIGINT (and others), and resend the signal (using the kill() function) to the process you want to receive it.

unexpected termination of program running in a loop

This is a cleaned code that I'm using in order to execute shell commands.
Though isExit is always 0 and loop should run as long as it is !isExit, my program terminates after one loop with the command ls as argument to execute.
Does anyone have any idea?
The output is OK (ls) but then program terminates.
The code is written in C, on Eclipse.
Running on Ubuntu 12 which is running on VM over Windows 7.
int main() {
int numberOfCommands, numOfWords, i, isExit = 0, isBackGround = 0, isSucces;
char input[256];
char *cmdAndArgs[256];
char *commands[256];
do{
// gets and parses user command...
ExecuteCmd(cmdAndArgs);
} while (!isExit);
return EXIT_SUCCESS;
}
void ExecuteCmd(char *cmdAndArgs[]){
pid_t pid;
pid = fork();
if (pid != 0) {
execvp(cmdAndArgs[0], cmdAndArgs);
exit(0);
} else {
waitpid(pid, &status, 0);
}
}
You're running the execvp in the parent process, not in the child. the logic:
pid_t pid;
pid = fork();
if (pid != 0) {
execvp(cmdAndArgs[0], cmdAndArgs);
exit(0);
} else {
waitpid(pid, &status, 0);
}
should be reversed to:
pid_t pid;
pid = fork();
if (pid == 0) { /* Child */
execvp(cmdAndArgs[0], cmdAndArgs);
exit(0);
} else if (pid == -1) {
perror("fork");
} else {
waitpid(pid, &status, 0);
}
the return codes from fork() are: -1 == fork failed (use errno to determine the reason). 0 == I am the child. > 0 == I am the parent. See the reference for fork under the RETURN VALUE section.
Is it possible that you have a buffer overflow in // gets and parses user command...? If you write past the allocated space, you can end up overwriting the value of isExit

Resources