Basic shell implementation
Allocation and freeing

In the while loop of your infinite loop, you are allocating memory for proc only to be completely freeing it by the end of the loop. Why? Considering every loop you reset with memset, why not keep the same allocated memory for the entire time?

Your code would be much faster if you moved the allocation of proc out of both loops, and if you did not free the memory every iteration, too. Also, you seem to be freeing proc in a few other places in the loop; see if you can remove those, too.



Refactor built-in functions

Here you are checking for commands that require special attention:


if(!strcmp(proc-&gt;commv[0], "exit")) {
    free(proc);
    exit(EXIT_SUCCESS);
}

if(!strcmp(proc-&gt;commv[0], "cd")) {
    proc-&gt;exec = 0;
    if (chdir(proc-&gt;commv[1]) != 0)
        error_msg("chdir() error");
}



What if you need to add another command to this list for whatever reason? Now, you could just add another if, but then your code starts to look a little  more messy.

To make your code more flexible here, you could try splitting these built-in functions into a struct which stores the name of the built-in function and a pointer to a function to execute if this is come across.

Then, you could create a function that checks if the command entered is a built-in function. If it is, then the function can easily handle the situation using the built-in function's struct.

Here is a short snippet of what I mean:

struct builtin {
    char *name;
    function pointer;
} builtins = {
    "exit", builtin_exit,
    ...
};


Then, all you need is a function to check through builtins to see if the command entered is a built-in function.

From there, if you ever need to add another built-in function, you simply add another one to the array (and create its respective function), and you don't even have to touch the shell's main code.



Exit number

In your error_msg function, you have this:


exit(0);



Well, that doesn't make much sense; if there's an error, the code needs to return a non-zero number to indicate failure. Other than that, the environment in which this program was run will not be able to tell that there was an error.



const int or #define


#define MAX_LEN 4096
#define MAX_ARGS 256



It would be better to use a const int here because that will actually define a type for the variable, where #define simply sticks that literal string after the macro name into the code and leaves it up to the compiler to determine a type for it.