5

I'm currently diving into creating a backgrounding a job in C with &. I need to implement a non-blocking waitpid in order for this to work. I know that. Also, I already am catching the condition if & is entered at the end of the command line. I'm just not sure how to exactly send the process with the end to be a background job and also implement it as executing while another prompt is prompting for the next command.
Anything would help at all, thank you.

    struct bgprocess{
        int pid;
        struct bgprocess * next;
        struct bgprocess * prev;    
    };

    struct bgprocess * bgprocess1;
    bgprocess1 = malloc(sizeof(struct bgprocess));
    bgprocess1->prev = NULL;
    bgprocess1->next = NULL;
    bgprocess1->pid = NULL;

    struct bgprocess * current;
    current = bgprocess1;

    do{
        int bgreturn = 0;
        while (current != NULL){
            if (waitpid(current->pid, &bgreturn, WNOHANG)){
                printf("Child exited");
                current->prev->next = current->next;
                current->next->prev = current->prev;
                current->prev = NULL;
                current->next = NULL;
                free(current);                  
            }
        current = current->next;    
        }
        if (end){
            int pid = fork();
            if (pid < 0){
                exit(1);            
            }       
            if (pid) {
                execvp(args[0], args);          
                exit(0);            
            }

            struct bgprocess * newNode;
            newNode = malloc(sizeof(struct bgprocess));
            newNode->pid = pid;
            newNode->next = NULL;

            if (current->next == NULL){
                current->next = newNode;        
            }
            while (1){
                if (current->next == NULL){
                    current->next = newNode;
                }           
                current = current->next;
            }

        }
    }
    while (current != NULL);

    int bgreturn = 0;
    while (current != NULL){
        if (waitpid(current->pid, &bgreturn,0)){
            printf("Child exited");
            current->prev->next = current->next;
            current->next->prev = current->prev;
            current->prev = NULL;
            current->next = NULL;
            free(current);  
        }
        current = current->next;
    }
    }

Alright, so I've been working on this some more and I think I may be starting to understand. I still have a few syntax errors that I'm unaware of how to fix so I'll probably use gdb or something unless someone else can point them out. Am I going about it the right way or am I completely wrong?

  • Are you looking to execute an external linux command from your C code in background ? – MOHAMED Oct 10 '12 at 17:22
  • I'm looking to execute external commands on a solaris system from my own shell that is written in C. –  Oct 10 '12 at 17:23
  • possible duplicate of [Waitpid equivalent with timeout?](http://stackoverflow.com/questions/282176/waitpid-equivalent-with-timeout) – s4y Oct 10 '12 at 17:24
  • Maybe you are interested into a http://stackoverflow.com/questions/1618756/how-do-i-exec-a-process-in-the-background-in-c – b3h3m0th Oct 10 '12 at 17:25
  • did you tried something like that `system("external command &");` ? – MOHAMED Oct 10 '12 at 17:26
  • No, I haven't tried anything like that. I need to implement everything myself. –  Oct 10 '12 at 17:27
  • You've clearly not included everything. For instance what is `end`? `pathlist`? Why are your forking ten child processes? Why are you using WNOHANG, when it seems like you actually want your process to hang and wait on those sleeping processes? You said below it's printing 'ls'? Where? There are no print statements here. – FrankieTheKneeMan Oct 10 '12 at 17:40
  • sorry, I left some stuff out. I also had an execvp in my first for loop for whatever command I want to be executed. My pathlist is my list of directories that I'm allowed to search for external commands. End is set to "&" if "&" is the end of my commandline that I have entered. I thought that the ten child processes would work considering if I'm having several background processes going on at once. And I don't know about using WNOHANG at all, so I'm hoping you can explain that to me. I'm looking to not block anything so I thought that would work. –  Oct 10 '12 at 17:46
  • From the man page (which I linked you to in my response, but you could look up on your system by typing `man wait`): 'WNOHANG: return immediately if no child has exited' - meaning it doesn't wait, it just checks to see what's going on with that child. Do yourself a favor and always read the MAN page. Top to bottom. They're pretty well written. – FrankieTheKneeMan Oct 10 '12 at 17:59
  • @Requiem, please post your complete code if you want more help. – FrankieTheKneeMan Oct 10 '12 at 18:59
  • @FrankieTheKneeMan posted my code again to see if I'm on the right track or not. I'm getting a few weird syntax errors that I've been trying to figure out to make it compile. –  Oct 11 '12 at 15:41
  • @FrankieTheKneeMan redid the code further. Got rid of all my syntax errors. Now just getting a segfault around my removing node. –  Oct 11 '12 at 16:17
  • That makes sense. You're not dealing with edges properly. For instance: If there's only one node in the list, `current->prev` and `current->next` are null. And if there are none, what then? Also, why are you doing a while(1)? That loop will necessarily walk off the end of the array. – FrankieTheKneeMan Oct 11 '12 at 16:49
  • Yeah, I got rid of while(1) shortly after I posted the code. I have no clue why I put that there honestly. So I need to basically make a temp in order to remove a node properly? –  Oct 11 '12 at 18:24
  • I'm assuming everything else looks pretty solid though? I think I figured out mostly everything it's just fixing that remove issue and also going through and placing the prompt in the right spot. –  Oct 11 '12 at 18:25

1 Answers1

11

Sounds like you're implementing a shell.

Just use fork to create a child process - which will be concurrent. From there you can use the exec* family to execute whatever executables you like, or simply run C code in the child while the parent goes back and prompts for more information (next command, etc.) Use wait with the WNOHANG option at the top of the loop to check for terminated children, and again (this time without WNOHANG) at the end for the rest of those children.

I must encourage you not to make this more complicated than it really is. Write out what you want in plain english (or your native language, or pseudocode), then merely translate that into C with the minimum amount of cleverness.

(pseudo)Code:

struct child {
    int pid;
    struct child * next;
    struct child * prev;
}
struct child * children = null;
do {
    int return = 0;
    struct child * curr = children;
    while(curr != null){
        if(waitpid(curr->pid, &return, WNOHANG)){
            //Report child exited with return status 'return'
            //Remove child (linked list style)
         }
         curr = curr->next;
     }
     /* PROMPT, ETC */
     if ( doInBackground ){
         int pid = fork();
         if(pid <0 )exit(); //error
         if(pid){
             //Child
             execvp(processName, arrayOfArgs);
             //This should never get executed
             exit();
         }
         //Add pid (linked list style, again)
     }
}while(!exitCondition)

int return = 0;
struct child * curr = children;
while(curr != null){
    if(waitpid(curr->pid, &return, 0)){
        //Report child exited with return status 'return'
        //Remove child (linked list style)
    }
    curr = curr->next;
}
FrankieTheKneeMan
  • 6,645
  • 2
  • 26
  • 37
  • Also, I have to make sure that there are no zombies left behind so that's why I believe that I need to use the waitpid. –  Oct 10 '12 at 17:26
  • fork returns a pid to the parent. Maintain a list of child PIDs, and when the shell exits, you can simply wait on each of them. At the top of the control loop, simply use wait with WNOHANG to check for any terminated processes to remove them from your list of active child PIDs. – FrankieTheKneeMan Oct 10 '12 at 17:28
  • Would it help if I posted the code I have so far so that you can get an idea of how I'm going about it? Cause I'm a little confused right now. –  Oct 10 '12 at 17:30
  • That will literally always help. But don't put it in the comments - edit your original question. – FrankieTheKneeMan Oct 10 '12 at 17:31
  • Alright, I placed the code in there. For some reason, let's say I type "ls &", it just keeps printing ls over and over again. –  Oct 10 '12 at 17:34
  • @Requiem - Added Pseudocode. Note that in the real world, just because `waitpid` returns, doesn't means an exit. Check them man page for better error checking. – FrankieTheKneeMan Oct 10 '12 at 18:03
  • Added my new code to the post. Just wanted to see if I'm heading in the right direction or not. –  Oct 11 '12 at 02:41