Link to home
Start Free TrialLog in
Avatar of Sambho
Sambho

asked on

About system call and managing processes in linux

Hi guys,
               I got this program written in c and I am running on linux but I have difficulty in understanding on what this program is doing. Moreover, I don't know what kind of command line input should be given after running this program....

So, guys can you help me what kind of input should be given and basic understanding of the result it is giving.........

It would be nice if I get brief insight on this program.....
           
                                  Tons of thanks in advance guys......
/* LIBRARY SECTION */
#include <ctype.h>              /* Character types                       */
#include <stdio.h>              /* Standard buffered input/output        */
#include <stdlib.h>             /* Standard library functions            */
#include <string.h>             /* String operations                     */
#include <sys/types.h>          /* Data types                            */
#include <sys/wait.h>           /* Declarations for waiting              */
#include <unistd.h>             /* Standard symbolic constants and types */

#include "smp1_tests.h"         /* Built-in test system                  */

/* DEFINE SECTION */
#define SHELL_BUFFER_SIZE 256   /* Size of the Shell input buffer        */
#define SHELL_MAX_ARGS 8        /* Maximum number of arguments parsed    */

/* VARIABLE SECTION */
enum { STATE_SPACE, STATE_NON_SPACE };	/* Parser states */


int imthechild(const char *path_to_exec, char *const args[])
{
	return execv(path_to_exec, args) ? -1 : 0;
}

void imtheparent(pid_t child_pid, int run_in_background)
{
	int child_return_val, child_error_code;

	/* fork returned a positive pid so we are the parent */
	fprintf(stderr,
	        "  Parent says 'child process has been forked with pid=%d'\n",
	        child_pid);
	if (run_in_background) {
		fprintf(stderr,
		        "  Parent says 'run_in_background=1 ... so we're not waiting for the child'\n");
		return;
	}
	wait(&child_return_val);
	/* Use the WEXITSTATUS to extract the status code from the return value */
	child_error_code = WEXITSTATUS(child_return_val);
	fprintf(stderr,
	        "  Parent says 'wait() returned so the child with pid=%d is finished.'\n",
	        child_pid);
	if (child_error_code != 0) {
		/* Error: Child process failed. Most likely a failed exec */
		fprintf(stderr,
		        "  Parent says 'Child process %d failed with code %d'\n",
		        child_pid, child_error_code);
	}
}

/* MAIN PROCEDURE SECTION */
int main(int argc, char **argv)
{
	pid_t shell_pid, pid_from_fork;
	int n_read, i, exec_argc, parser_state, run_in_background;
	/* buffer: The Shell's input buffer. */
	char buffer[SHELL_BUFFER_SIZE];
	/* exec_argv: Arguments passed to exec call including NULL terminator. */
	char *exec_argv[SHELL_MAX_ARGS + 1];

	/* Entrypoint for the testrunner program */
	if (argc > 1 && !strcmp(argv[1], "-test")) {
		return run_smp1_tests(argc - 1, argv + 1);
	}

	/* Allow the Shell prompt to display the pid of this process */
	shell_pid = getpid();

	while (1) {
	/* The Shell runs in an infinite loop, processing input. */

		fprintf(stdout, "Shell(pid=%d)> ", shell_pid);
		fflush(stdout);

		/* Read a line of input. */
		if (fgets(buffer, SHELL_BUFFER_SIZE, stdin) == NULL)
			return EXIT_SUCCESS;
		n_read = strlen(buffer);
		run_in_background = n_read > 2 && buffer[n_read - 2] == '&';
		buffer[n_read - run_in_background - 1] = '\n';

		/* Parse the arguments: the first argument is the file or command *
		 * we want to run.                                                */

		parser_state = STATE_SPACE;
		for (exec_argc = 0, i = 0;
		     (buffer[i] != '\n') && (exec_argc < SHELL_MAX_ARGS); i++) {

			if (!isspace(buffer[i])) {
				if (parser_state == STATE_SPACE)
					exec_argv[exec_argc++] = &buffer[i];
				parser_state = STATE_NON_SPACE;
			} else {
				buffer[i] = '\0';
				parser_state = STATE_SPACE;
			}
		}

		/* run_in_background is 1 if the input line's last character *
		 * is an ampersand (indicating background execution).        */


		buffer[i] = '\0';	/* Terminate input, overwriting the '&' if it exists */

		/* If no command was given (empty line) the Shell just prints the prompt again */
		if (!exec_argc)
			continue;
		/* Terminate the list of exec parameters with NULL */
		exec_argv[exec_argc] = NULL;

		/* If Shell runs 'exit' it exits the program. */
		if (!strcmp(exec_argv[0], "exit")) {
			printf("Exiting process %d\n", shell_pid);
			return EXIT_SUCCESS;	/* End Shell program */

		} else if (!strcmp(exec_argv[0], "cd") && exec_argc > 1) {
		/* Running 'cd' changes the Shell's working directory. */
			/* Alternative: try chdir inside a forked child: if(fork() == 0) { */
			if (chdir(exec_argv[1]))
				/* Error: change directory failed */
				fprintf(stderr, "cd: failed to chdir %s\n", exec_argv[1]);	
			/* End alternative: exit(EXIT_SUCCESS);} */

		} else {
		/* Execute Commands */
			/* Try replacing 'fork()' with '0'.  What happens? */
			pid_from_fork = fork();

			if (pid_from_fork < 0) {
				/* Error: fork() failed.  Unlikely, but possible (e.g. OS *
				 * kernel runs out of memory or process descriptors).     */
				fprintf(stderr, "fork failed\n");
				continue;
			}
			if (pid_from_fork == 0) {
				return imthechild(exec_argv[0], &exec_argv[0]);
				/* Exit from main. */
			} else {
				imtheparent(pid_from_fork, run_in_background);
				/* Parent will continue around the loop. */
			}
		} /* end if */
	} /* end while loop */

	return EXIT_SUCCESS;
} /* end main() */

Open in new window

Avatar of phoffric
phoffric

If you could narrow down where you need assistance (i.e., give LOC and question), then we may be able to help you better. Otherwise, there are potentially over 147 questions (one for each LOC, and more for relationships between LOCs).

You did ask one specific question which I will try to help you with:
>> I don't know what kind of command line input should be given after running this program....

Below is your code (LOCs 62-65):
If there are no arguments in the command line, then argc is 1, and argc > 1 is false, so the next line
    return run_smp1_tests(argc - 1, argv + 1);
will *not* be executed.

If there are one or more arguments with the first one being "-test" as in:
    $ ./a.exe -test more-arguments-as-needed
then the run_smp1_tests() function is called with one less argument (for the first parameter), and the argv pointer incremented by 1, thereby skipping the first string value (i.e., in my example, that would be "./a.exe").

I cannot say what run_smp1_tests() does since it is probably defined in your smp1_tests.h include file, which is not posted.

It appears that if the arguments are described as above, then run_smp1_tests() is called, and after completing, it returns a status, which is passed upwards to the shell that invoked the program. The rest of the program in main() is not executed since the program has a return.

If the arguments are not as described above, then run_smp1_tests() is not called, and the rest of the program is executed.

I hope this helps a little. I realize that there are potentially another 140 questions, and I have only addressed one.
	/* Entrypoint for the testrunner program */
	if (argc > 1 && !strcmp(argv[1], "-test")) {
		return run_smp1_tests(argc - 1, argv + 1);
	}

Open in new window

Avatar of Sambho

ASKER

Thank you phoffric,
                               but still I am not getting it... I am going through the program..I know there are lots of questions to be asked in the program. Can you tell me what is this program in overall trying to do.......

I compile shell executable and......

The result of the program is something like this...



$ ./shell
Shell(pid=593)> -test
  Parent says 'child process has been forked with pid=594'
  Parent says 'wait() returned so the child with pid=594 is finished.'
  Parent says 'Child process 594 failed with code 255'
Shell(pid=593)> 23
  Parent says 'child process has been forked with pid=600'
  Parent says 'wait() returned so the child with pid=600 is finished.'
  Parent says 'Child process 600 failed with code 255'

what does this result actually mean? Does the child process always gets finished instantly after getting forked and why is child process always failing with code 255.....  

I have attached the smp1.tests.c

Thank You very much in advance.....
#define _GNU_SOURCE
#include <stdio.h>
#undef _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "testrunner.h"
#include "smp1_tests.h"

#define quit_if(cond) do {if (cond) exit(EXIT_FAILURE);} while(0)

/* Prepare input, reroute file descriptors, and run the program. */
void run_test(const char *input, int argc, char **argv)
{
	/* Prepare input */
	FILE *in = fopen("smp1.in", "w");
	fprintf(in, input);
	fclose(in);
	/* Reroute standard file descriptors */
	freopen("smp1.in",  "r", stdin );
	freopen("smp1.out", "w", stdout);
	freopen("smp1.err", "w", stderr);
	/* Run the program */
	quit_if(main(argc, argv) != EXIT_SUCCESS);
	fclose(stdout);
	fclose(stderr);
}

/* P5.1: Test of executing commands in the path */
int test_path(int argc, char **argv)
{
	char *args[] = { "./shell", NULL };
	FILE *out, *err;
	int pid_tmp;
	/* Run the test */
	run_test("ls\n/bin/ls\nexit\n", 1, args);
	/* Check output */
	err = fopen("smp1.err", "r");
	quit_if(fscanf(err, "  Parent says 'child process has been forked with pid=%d'\n"
	                    "  Parent says 'wait() returned so the child with pid=%d is finished.'\n"
	                    "  Parent says 'child process has been forked with pid=%d'\n"
	                    "  Parent says 'wait() returned so the child with pid=%d is finished.'\n",
	               &pid_tmp, &pid_tmp, &pid_tmp, &pid_tmp) != 4);
	quit_if(!feof(err));
	fclose(err);
	return EXIT_SUCCESS;
}

/* P5.2: Test of command line counter */
int test_counter(int argc, char **argv)
{
	char *args[] = { "./shell", NULL };
	FILE *out, *err;
	int pid_tmp;
	/* Run the test */
	run_test("\n/bin/true\nexit\n", 1, args);
	/* Check output */
	out = fopen("smp1.out", "r");
	quit_if(fscanf(out, "Shell(pid=%d)1> Shell(pid=%d)1> Shell(pid=%d)2> Exiting process %d\n", &pid_tmp, &pid_tmp, &pid_tmp, &pid_tmp) != 4);
	quit_if(!feof(out));
	fclose(out);
	return EXIT_SUCCESS;
}

/* P5.3: Test of re-executing earlier commands */
int test_rerun(int argc, char **argv)
{
	char *args[] = { "./shell", NULL };
	FILE *out, *err;
	int pid_tmp;
	/* Run the test */
	run_test("/bin/echo test\n!1\nexit\n", 1, args);
	/* Check output */
	out = fopen("smp1.out", "r");
	quit_if(fscanf(out, "Shell(pid=%d)1> test\nShell(pid=%d)2> test\nShell(pid=%d)3> Exiting process %d\n", &pid_tmp, &pid_tmp, &pid_tmp, &pid_tmp) != 4);
	quit_if(!feof(out));
	fclose(out);
	return EXIT_SUCCESS;
}

/* P5.5: Test of depth-limited sub */
int test_sub(int argc, char **argv)
{
	char *args[] = { "./shell", NULL };
	FILE *out, *err;
	int pids[4], warned_too_deep;
	/* Run the test */
	run_test("sub\nsub\nsub\nexit\nexit\nexit\n", 1, args);
	/* Check output */
	out = fopen("smp1.out", "r");
	err = fopen("smp1.err", "r");
	/* First, check that the subshells were invoked. */
	fscanf(out, "Shell(pid=%d)1> Shell(pid=%d)1> Shell(pid=%d)1> Shell(pid=%d)2> ", &pids[0], &pids[1], &pids[2], &pids[3]);
	quit_if(!((pids[0] != pids[1]) && (pids[1] != pids[2]) && (pids[0] != pids[2]) && (pids[2] == pids[3])));
	/* Next, check for the "Too deep!" message: */
	warned_too_deep = 0;
	/* Use a while loop because multiple processes write to stderr concurrently. */
	while (!warned_too_deep && !feof(err)) {
		char too_deep[11];
		fgets(too_deep, 11, err);
		if (!strncmp(too_deep, "Too deep!\n", 10))
			warned_too_deep = 1;
	}
	quit_if(!warned_too_deep);
	fclose(out);
	fclose(err);
	return EXIT_SUCCESS;
}

/*
 * Main entry point for SMP1 test harness
 */
int run_smp1_tests(int argc, char **argv)
{
	/* Tests can be invoked by matching their name or their suite name 
	   or 'all' */
	testentry_t tests[] = {
		{ "sub",     "smp1", test_sub     },
		{ "rerun",   "smp1", test_rerun   },
		{ "counter", "smp1", test_counter },
		{ "path",    "smp1", test_path    } };
	int result = run_testrunner(argc, argv, tests, sizeof(tests) / sizeof (testentry_t));
	unlink("smp1.in");
	unlink("smp1.out");
	unlink("smp1.err");
	return result;
}

Open in new window

I apologize for not having enough time tonight to answer more questions. In about 3 hours, more experts should arrive on the scene.
Avatar of Sambho

ASKER


Thats OK no problem....I hope somebody will tell me something...regarding this.....

Well lets wait and see......
If you could post the code that is missing, that may be helpful. Then an expert could actually build the program and witness the failures you are showing. Perhaps you are missing some command line arguments after the -test argument that are necessary for all the different tests.If you step through the program (I use ddd; and sometimes insight), then you can see where the called function returns an error. Then, if you are still not understanding the code, you can point to the area that you need help with. I'll be back late tonight and will try to respond to your full listing tomorrow. But I hope you'll be able to get a quicker response if you list the entire program so that they can build it and see what is wrong.
Avatar of Sambho

ASKER


There are like 5 or 6 files including header files..............So shall I post all the files or just the one with .c extensions. I have 3 files with .c extensions... ( shell.c, testrunner.c and smp1_tests.c)....


Thank You...
Can you put into a zip file the entire project: source code (.h and .c) and also the makefile? If there are any input files needed, include them as well, but change extension to .txt (and give instructions on original filenames). Include the folder paths, if possible; otherwise, give instructions as to how the files fan out into a folder tree.
Avatar of Sambho

ASKER


OK Phoffric....as you said I have created a zip file with .txt extension of all the files and attached here.......


Please have a look.......


Thank You
program.zip
program.txt
Hi sambho,

I took a quick look at your project, and online as well. I see that this project is academic in nature. If this is an academic project that you are working on, could you please let me know. You are aware of the EE Academic Policy that includes self-study; so that is why I am asking you. Thanks.

The make built OK.
Avatar of Sambho

ASKER

Yes this project is very much academic in nature.......but this is not homework or an assignment problem.

                                       This is just a random project I picked up for learning purpose which I want to learn very much..But I don't know how to play with this project or how to get desired results from it or more specifically what kind of command line arguments should be given to analyze the result and learn the functionalities of the program.....

Your help will be appreciated....Thank You........
>> $ ./shell
>> Shell(pid=593)> -test

The -test option is a to be used external to the shell prompt. That is:
$ ./shell -test

If using the shell prompt, then you can issue unix commands; for example:

$ ./shell
Shell(pid=2268)> ls -lrt
imthechild: calling execv with ls, args[0] = ls <--- Note: I added this debug to verify fork OK
  Parent says 'wait() returned so the child with pid=1752 is finished.'
  Parent says 'Child process 1752 failed with code 255'
Shell(pid=2268)> exit
Exiting process 2268
That failed because the path in the shell didn't have the path set up correctly. So, I'll include the full path:

Shell(pid=868)> /usr/bin/ls -lrt


imthechild: calling execv with /usr/bin/ls, args[0] = /usr/bin/ls
total 153
-rwxrwxrwx 1 ddd None  6386 2007-11-22 15:05 testrunner.c
-rwxrwxrwx 1 ddd None   104 2007-11-22 15:05 b.sh
-rwxrwxrwx 1 ddd None   404 2007-11-22 15:05 Makefile.orig
-rwxrwxrwx 1 ddd None   368 2007-11-22 15:05 testrunner.h
-rwxrwxrwx 1 ddd None   154 2007-11-22 15:05 smp1_tests.h
-rwxrwxrwx 1 ddd None  4019 2007-11-22 15:05 smp1_tests.c
-rwxrwxrwx 1 ddd None  5011 2007-11-26 23:00 shell.c_orig
-rwxrwxrwx 1 ddd None  5176 2007-12-01 15:55 README.txt
-rwxrwxrwx 1 ddd None  9247 2010-10-12 01:32 program.zip
-rwxrwxrwx 1 ddd None  9247 2010-10-12 01:32 program1.zip
-rwxrwxrwx 1 ddd None   410 2010-10-12 15:42 Makefile
-rw-r--r-- 1 ddd None 10423 2010-10-12 15:43 smp1_tests.o
-rw-r--r-- 1 ddd None 12757 2010-10-12 15:43 testrunner.o
-rwxrwxrwx 1 ddd None  5319 2010-10-13 08:07 shell.c
-rw-r--r-- 1 ddd None  5526 2010-10-13 08:07 shell.o
-rwxr-xr-x 1 ddd None 50531 2010-10-13 08:07 shell.exe
  Parent says 'wait() returned so the child with pid=3936 is finished.'
Shell(pid=868)> exit
Exiting process 868
Avatar of Sambho

ASKER

Thanks a lot Phofiric,
                                    Now I know how to start studying this program.........Ok so basically I can implement linux commands here, right? The program basically shows how fork() creates child process to run the input command i,e argv[]... IS it true or is it little different than that...

But I have a problem.....When I issue same command it doesn't run.... it says.....
]$ ./shell
Shell(pid=20403)> /usr/bin/ls -lrt
  Parent says 'child process has been forked with pid=20407'
  Parent says 'wait() returned so the child with pid=20407 is finished.'
  Parent says 'Child process 20407 failed with code 255'

I don't know what is the problem.....

Thank You Phoffric.......for your help.......
Maybe my Cygwin is different than your *nix version??

From your regular shell, try "whereis" and "which" to see where you might find your ls command. (Not all *nix versions have both "whereis" and "which".

$ whereis ls
ls: /bin/ls.exe /usr/bin/ls.exe /usr/share/man/man1/ls.1.gz

$ which ls
/usr/bin/ls

For example, this also worked for me:
Shell(pid=1300)> /bin/ls -lrt
>> so basically I can implement linux commands here, right? The program basically shows how fork() creates child process to run the input command i,e argv[]... IS it true or is it little different than that...

For the one case we talked about, yes, the command and its command line arguments get filled into argv[] and then can be executed by the shell that you defined. Look at the code, and you see special code for "cd" and "exit", for example. You can now make up any user defined commands and add them into this list.

The -test option shows other things that this program can do, but I haven't looked at that in any detail.
Avatar of Sambho

ASKER


Oh yes I found it in my system............it is

Shell(pid=26266)> /bin/ls -lst
  Parent says 'child process has been forked with pid=26269'
total 132
 8 -rwxrwxr-x 1 ssapkota ssapkota  5227 Oct 10 18:06 fork02
 4 -rw-rw-r-- 1 ssapkota ssapkota  1659 Oct 10 18:05 Fork02.c
 8 -rwxrwxr-x 1 ssapkota ssapkota  5124 Oct 10 17:44 fork

And also Mr. Phoffric........I tried cd command but I could not run cd command and I could not find the location of CD command....

i looked in the code but could not find the code for the CD command...if you have time then can you show me that piece of code for cd command...


Thank You very much...............
Avatar of Sambho

ASKER



Oh I found.........code for CD also and implemented that one too..

 Shell(pid=26598)> cd /bin
Shell(pid=26598)> /bin/ls -lst
  Parent says 'child process has been forked with pid=26601'
total 7480
1072 -rwxr-xr-x 1 root root 1089956 Jul 22  2009 ksh
   4 lrwxrwxrwx 1 root root       4 May 20  2009 mailx -> mail
   4 lrwxrwxrwx 1 root root       2 May 20  2009 ex -> vi
   4 lrwxrwxrwx 1 root root       2 May 20  2009 rvi -> vi
   4 lrwxrwxrwx 1 root root       2 May 20  2009 rview -> vi
   4 lrwxrwxrwx 1 root root       2 May 20  2009 view -> vi
   4 lrwxrwxrwx 1 root root       4 May 20  2009 csh -> tcsh
   4 lrwxrwxrwx 1 root root       3 May 20  2009 gtar -> tar
   4 lrwxrwxrwx 1 root root       8 May 20  2009 dnsdomainn

I will ask you few more questions little later.....

Thank You
ASKER CERTIFIED SOLUTION
Avatar of phoffric
phoffric

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Sambho

ASKER

oh yes I understand..................I will end this thread right here......and start same discussion on the new thread......

                    though , I have one question to ask that you mentioned earlier about -test.....

You said...
>> $ ./shell
>> Shell(pid=593)> -test

The -test option is a to be used external to the shell prompt. That is:
$ ./shell -test

But when I did..........

$ ./shell -test
Test '(empty)' not found
Valid tests : all sub rerun counter path
Valid suites: smp1

I don't understand what it means? i understood it should be used external to the shell prompt but what does it mean by Valid tests : all sub rerun counter path......

Thank You......
In my first post, I pointed out that when -test option is used, then the run_smp1_tests function is called (otherwise it is not called).

This function has a table:
     testentry_t tests[]
which shows the test entry points for a suite of tests. We would have to look at each entry point and the run_testrunner function which exercises each test to fully understand what is going on. Some of your results requires a little searching, sometimes with not obvious results:

For example:
$ grep "counter" *
README.txt:  2. Modify this MP so that the command prompt includes a counter that
README.txt:       "Shell(pid=%1)%2> "  %1=process pid %2=counter
README.txt:     Do not increment the counter if no command is supplied to execute.
README.txt:     Test: ./shell -test counter
Binary file shell.exe matches
smp1_tests.c:/* P5.2: Test of command line counter */
smp1_tests.c:int test_counter(int argc, char **argv)
smp1_tests.c:           { "counter", "smp1", test_counter },
Binary file smp1_tests.o matches

I suggest adding printf trace and debug statements so that you know you are in this test suite, and print out some useful values. It will take a bit of work and detailed examination of all the source code you provided. But just do one test at a time, and see what the purpose of each one is.
Avatar of Sambho

ASKER

thank You very much Phoffric for your time and explanations............

               
You are very welcome. Hope it helped.
i keep trying to track the code that actually performs the four tests in the make test but i feel like its just passing arguements to various functions that never end up really doing anything... help!