Saturday, November 15, 2008

PROCESS1(LINUX)

Book to be followed:

"Advanced Programming in the UNIX Environment" by W.Richard Stevens.

Create a directory [ process1 ] in your home directory and carry out
this experiment in that directory.
$ mkdir $HOME/process1
$ cd $HOME/process1

Down-load [process1.txt] in that directory
$ ftp IP-address-of-FTP-server

The system calls [getpid], [getppid], [getuid], [geteuid], [getgid],
[getegid], [fork], [execve], and [execl] are introduced in this
experiment.

Process control in UNIX involves creation of new process, executing
programs and process termination.

Every process is identified uniquely by a nonnegative integer called
Process ID (PID).

Well known process ID: ID 0 is usually the [scheduler] process. It is
a system process and no program on disk corresponds to this process.
ID 1 is usually the [init] process, which is invoked by the kernel at
the end of the bootstrap procedure.
To check the process ID of [init] use command..
$ ps ax | grep init

You may read manual of [ps(1)] for the meaning of different fields of
the output.
$ man 1 ps

Some process related system calls:

[getpid] returns the process ID of the current process.
[getppid] returns the process ID of the parent of the current process.
[getuid] returns the real user ID of the current process.
[geteuid] returns the effective user ID of the current process.
[getgid] returns the real group ID of the current process.
[getegid] returns the effective group ID of the current process.

Save the following in a file [f1.c].
------------------------------------------------------------------
// file-name f1.c
#include
#include
#include
#include

int main(void)
{
printf("Process ID of current process = %d\n",getpid() );
printf("Process ID of parent process = %d\n",getppid() );
printf("Real user ID of current process = %d\n",getuid() );
printf("Effective user ID of current process = %d\n",geteuid() );
printf("Real group ID of current process = %d\n",getgid() );
printf("Effective group ID of current process = %d\n",getegid());
for(; ; ) { sleep(1); } //Endless loop.

return 0;
}
------------------------------------------------------------------

Assignment-1(a):

What is the name of the process corresponding to PID = 1 ?
Process name ---->

Compile [f1.c]
$ gcc -Wall ./f1.c -o ./P1

[ ./P1 ] is to be copied in [ /usr/local/bin/ ] directory, to act as a
new UNIX command, which any user can execute. An ordinary user does
not have the permission to write in [ /usr/local/bin/ ] directory.

As superuser, copy [P1] to [/usr/local/bin/] directory
# cp ./P1 /usr/local/bin/P1

Exit superuser mode.
# exit

Remove the original executable in the present directory
$ rm ./P1

Execute the program from your account. Do not terminate the program by
pressing CTRL-C
you $ /usr/local/bin/P1

If [ /usr/local/bin ] is in the [ PATH ] environmental variable, the
program can be executed as
you $ P1

Login in terminal-3 as another user, such as [sumit] or [ guest ] and
execute [ P1 ]. Do not terminate the program by pressing CTRL-C.
guest $ /usr/local/bin/P1


Login in terminal-4 as [guest] and in terminal-5 as [you] for checking
the results of the two running programs and fill up the following
table with pencil.
----------------------------------------------------------------------
(You) user name -> |(Another user) user name ->
----------------------------------------------------------------------
pid ppid uid euid gid egid | pid ppid uid euid gid egid
----------------------------------------------------------------------
| | | | | | | | | | |
----------------------------------------------------------------------

----------------------------------------------------------------------
Use [$ less /proc/pid/status] | Use [$ less /proc/pid/status]
program name,pid,ppid,uid,gid | program name,pid,ppid,uid,gid
matched ? ( Y / n ) | matched ? ( Y / n )
----------------------------------------------------------------------
uid,gid matched from | uid,gid matched from
[/etc/passwd] file ? ( Y / n ) | [/etc/passwd] file ? ( Y / n )
----------------------------------------------------------------------
pid and ttyx were verified from | pid and ttyx were verified from
the output of command | the output of command
[$ ps ax | grep P1] ? ( Y / n ) | [$ ps ax | grep P1] ? ( Y / n )
----------------------------------------------------------------------
Terminate the programs at terminals-2 and terminal-3 by CTRL-C.

Assignment-1(b).

As superuser, carry out the following commands...

Make [news] as owner of the program [/usr/local/bin/P1]
# chown news /usr/local/bin/P1

Make [mail] as group of the program [/usr/local/bin/P1]
# chgrp mail /usr/local/bin/P1

Set uid and gid bits of [/usr/local/bin/P1]
# chmod 6771 /usr/local/bin/P1
Exit superuser mode
# exit

Check the effect of above command with [ $ file /usr/local/bin/P1 ]


In terminal-2 execute the program; do not terminate
you $ P1

In terminal-3 execute the program; do not terminate
guest $ P1

Fill the following table
----------------------------------------------------------------------
(You) user name -> |(Another user) user name ->
----------------------------------------------------------------------
pid ppid uid euid gid egid | pid ppid uid euid gid egid
----------------------------------------------------------------------
| | | | | | | | | | |
----------------------------------------------------------------------
euid,egid matched from | euid,egid matched from
[/etc/passwd] file ? ( Y / N ) | [/etc/passwd] file ? ( Y / N )
----------------------------------------------------------------------
Terminate both programs by [CTRL-C].

Assignment-1(c).

Execute the command [ # /usr/local/bin/P1 ] as superuser and record..

pid-> ppid-> uid-> euid-> gid-> egid->

Use [CTRL-C] to terminate the program.
Remove the executable to clear system location. As this directory has
system files, interactive mode is used for file removal.
# rm -i /usr/local/bin/P1
Exit superuser mode.


Creation of a new process:
The only way to create a new process by UNIX
kernel is to call [fork] system call, by an existing process. The
special processes (such as [scheduler], [init] ) are exceptions, which
are created by kernel during bootstrapping. Process [init] happens to
be the parent of all processes. [init] also becomes the parent of any
orphaned (ignore now) child process. System call [fork] creates a
child process, that differs from the parent process in its PID and
PPID( among other properties ).

Only a few features of [fork] are illustrated in this laboratory
experiment. You must read manual for [fork(2)].

Example: Save the following in a file [f2.c]
-------------------------------------------------------------
// file-name f2.c
#include
#include
#include
#include

int main( void )
{
pid_t n;

n = fork();
if( n == -1 ) { perror("fork"); exit(1); }
// A [child] process is created if fork() call is successful

/*
If [ fork ] call is successful, a new process is created. It is
the child process. The variable [n] has different values for the
parent process and the child process. For the parent process the
value of [n] is the [pid] of the child process. The variable [n]
becomes zero to the child process. This is NOT the [pid] of the
child process. By using this value ( zero ), it is possible to
separate the blocks of code to be executed by the parent and the
child processes. This separation is not done in this example
program. As a result, the codes following [ fork ] call are
executed twice, once by the parent process and once by the child
process. Which executes first is not predictable.

Following lines are taken from [fork] man-page

RETURN VALUE
On success, the PID of the child process is returned in
the parent's thread of execution, and a 0 is returned in
the child's thread of execution. On failure, a -1 will be
returned in the parent's context, no child process will be
created, and [errno] will be set appropriately.
*/

printf("My pid = %u \n", n );

wait(NULL); // This statement would be explained later

return 0;
}
-------------------------------------------------------------

Compile, execute and record output
$ gcc -Wall ./f2.c -o ./two
$ ./two
My pid = ____ ( This is parent's / child's pid )
My pid = 0

Example:
Write a program to get the PID of parent and child process.
(WBUT2004-oslab)
------------------------------------------------
// file-name ex1.c
#include
#include
#include

int main( void )
{
pid_t pid;

pid = fork();
if( pid == -1 ) { perror("fork"); exit(1); }

if( pid == 0 ) // child process
{
exit(0); // child exits
}

// rest is parent process
printf("parent: My PID = %u \n", getpid() );
printf("parent: child's PID = %u \n", pid );

while(1) { sleep(1); } // endless loop

return 0;
}
------------------------------------------------
Compile and run...
$ gcc -Wall ./ex1.c -o ./example1
$ ./example1


In another terminal check with [ $ ps ax ] command.

Note: The child exited, but it's PID was still listed by OS. As the
parent is still running, OS is keeping the exit status of the
child ( and some other information ), for the parent to read.
This is an example of a zombie process.

Never use the name of executable similar to that of the source
code. Assume that [ ex1.c ] is a large source code program
written by you in final examination. You intend to use [ ex1 ]
as the mane of the executable file. Correct compilation command
is
$ gcc -Wall ./ex1.c -o ./ex1
While compiling you make a mistake as shown below...
$ gcc -Wall ./ex1.c -o ./ex1.c

Try this and find out what would be the problem.


Save the following in a file [f3.c].
----------------------------------------------------------------
// file-name f3.c

#include
#include
#include
#include
#include
#include
#include


#define FILEPATH "/etc/passwd"
#define BUFFERSIZE 512

int students = 30; // a global variable is initialised

int main(void)
{
pid_t x;
char buffer[BUFFERSIZE];
int sumit_age = 19;
int fd;
ssize_t i;


// A file is opened before [fork] call
fd = open( FILEPATH, O_RDONLY );
if( fd == -1 ) { perror("open"); exit(1); }

// parent process tries to create a new process
x = fork();
if ( x == -1 ) { perror("fork"); exit(2); }


if ( x == 0 ) // following block is executed by child process
{
students = students + 2;
sumit_age = sumit_age + 3 ;
printf("child: pid = %u.\n", getpid() );
printf("child: ppid = %u.\n", getppid() );
printf("child: uid = %u.\n", getuid() );
printf("child: euid = %u.\n", geteuid() );
printf("child: gid = %u.\n", getgid() );
printf("child: egid = %u.\n", getegid() );
memset( buffer,'\0', BUFFERSIZE );
i = read( fd, buffer, 100 );
write( STDOUT_FILENO, buffer, i );
printf( "\nchild: students = %u & sumit_age = %u \n",
students,sumit_age );
while (1) { sleep(1); } //endless loop
return 0;
}

// rest of the codes are executed by parent process
printf("parent: Going to sleep for 1 second \n" );
sleep(1);
printf("parent: child\'s pid = %u.\n", x );
printf("parent: pid = %u.\n", getpid() );
printf("parent: ppid = %u.\n", getppid() );
printf("parent: uid = %u.\n", getuid() );
printf("parent: euid = %u.\n", geteuid() );
printf("parent: gid = %u.\n", getgid() );
printf("parent: egid = %u.\n", getegid() );
memset( buffer,'\0', BUFFERSIZE );
i = read( fd, buffer, 100 );
write( STDOUT_FILENO, buffer, i );
printf( "\nparent: students = %d & sumit_age = %d\n",
students,sumit_age );
// parent closes all its file descriptors
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
close(fd);

return 0;
}
----------------------------------------------------------------

The parent is put to sleep for 1 second, to enhance the possibility
that the child enters the endless loop, by the time the parent starts
executing the code after [sleep(1)]. This is done for the sake of this
example only.

The program [f3.c] is intended to illustrate the following:

(1) The child inherits a copy of parent's data, ( copy on write ).
The child updates its copy of data [students (global variable)]
and [ sumit_age ]. This does not affect [ students ] and
[ sumit_age ] in parent's copy.
(2) The child inherits open file descriptors 0, 1, 2 and [fd] from
the parent. However parent and child share the same file offset
(file position pointer) of opened file/files. The parent opens
the file [/etc/passwd] and gets [fd], the file descriptor. The
descriptors 0, 1 and 2 were opened by OS for the main (parent).
The child inherits the four descriptors. The child reads 100
bytes from the inherited file descriptor [ fd ] and prints the
data on descriptor [1]. This advances the file offset, from its
initial position, by 100 bytes. Then the parent reads 100 bytes
from file descriptor [fd], using the current value of the file
offset and prints the data on descriptor [1].

Note:
If the parent and child write to the same file descriptor,
without any form of synchronisation, their output will be
intermixed.

(3) The child inherits real user ID [uid],effective user ID [euid],
real group ID [gid] and effective group ID [egid] from the
parent. The child's process ID [pid] and [ppid] are different
from those of the parent.
(4) It is possible for the child to continue, even after the parent
exits.


Assignment-2:
Compile [f3.c] and execute
$ gcc -Wall ./f3.c -o ./three
$ ./three

Though the program appears to end, child process would be running.

Fill-up the following table with pencil
----------------------------------------------------------------------
| pid | ppid | uid | euid | gid | egid | students | sumit_age |
----------------------------------------------------------------------
parent| | | | | | | | |
---------------------------------------------------------------------|
child | | | | | | | | |
----------------------------------------------------------------------

Though the [ parent ] closed all its file descriptors and then exited,
the file descriptors inherited by the [ child ] were not affected.

Check the file descriptors inherited by the [child] process
$ ls -l /proc/PID-of-child/fd

How many file descriptors the child had ? _____

End of Assignment-2.


The child process may replace itself with another process while
maintaining the same process ID. The child process may use the [exec]
family of library functions, that replaces the current process image
with a new process image. The following are the members of the [exec]
family of library functions : [execl], [execlp], [execle], [execv]
and [execvp]. You may read manual for [exec(3)].

The child may also use the system call [execve]. The [exec] family of
library functions use the [ execvp ] system call. You may read the
manual of [execve(2)].

Save the following in a file [f4.c]
-------------------------------------------------------------------
// file-name f4.c

#include
#include
#include
#include
#include

int main(void)
{
pid_t xx;
int n;
char *argv[] = { "-d 1", NULL };
// argument list terminated by a NULL pointer. Read execve(2).
char *argp[] = { "TERM=linux", NULL };
// environment list terminated by a NULL pointer. Read execve(2).


printf( "parent: My pid = %u \n", getpid() );
// main(parent) tries to create a new process
xx = fork();
if( xx == -1 ) { perror("fork-1"); exit(1); }

if ( xx == 0 ) // 1st-child process
{
printf("1st-child: My pid = %u \n", getpid() );
printf("1st-child: My ppid = %u \n", getppid() );
sleep(20);
printf("1st-child: Executing /usr/bin/top %s \n", argv[0]);
sleep(2);
n = execve ("/usr/bin/top", argv, argp);
if( n == -1 ) { perror("execve"); }
// [execve] never returns if successful
printf("[execve] ERROR!\n");
exit(2);
}

// parent process
// parent again tries to create a new process
xx = fork();
if( xx == -1 ) { perror("fork-2"); exit(3); }


if ( xx == 0 ) // 2nd-child process
{
printf( "2nd-child: My pid = %d \n",getpid() );
printf( "2nd-child: My ppid = %d \n",getppid() );
printf( "2nd-child: Executing shell script [./s1]\n" );
n = execl("./s1", NULL );
if( n == -1 ) { perror("execl"); }
// [execl] never returns if successful
printf("[execl ERROR !\n");
exit(4);
}

//parent process
wait(NULL);
wait(NULL);

return 0;
}
-------------------------------------------------------------------

Save the following in a file [./s1]
----------------------
#!/bin/bash

while : # endless loop
do
sleep 1
echo my PID = $$
done > ./s1.output
----------------------
Make the file executable by [chmod] command.
$ chmod 0500 ./s1

The program [f4.c] is intended to illustrate the following:

(1) More than one process can be forked by a process.
(2) Either a binary executable, or a shell script, can be executed by
[exec] family of functions.
When any process calls any of the [exec] functions, the calling
process is completely replaced by the new program, ( [exec] never
returns). The new program starts executing at its [main] program.
The process ID (pid) does not change across [exec] because a new
process is not created.

Assignment-3:
Compile [f4.c] and execute
$ gcc -Wall ./f4.c -o ./four
$ ./four

Within 20 seconds record the following:

parent: My pid =
1st-child: My pid =
1st-child: My ppid =
2nd-child: My pid =
2nd-child: My ppid =


In another terminal execute [ps ax] command and fill-up the following
table.

To find program names use the following command
$ less /proc/value-of-PID/status

-----------------------------------------
| | PID | Program name |
|-----------------------------------------|
| parent | | |
|-----------------------------------------|
| 1st-child | | |
|-----------------------------------------|
| 2nd-child | | |
-----------------------------------------


Terminate [top] with CTRL-C.

Examine file [s1.output] to check the PID of 2nd child.


End of Assignment-3.


-------------------------------------

Optional Assignment:
Since [fork] creates a new process, badly written
program, ( executed even from user account ) can affect the normal
working of a server.

Save the following program as [fork-bomb.c]
----------------------------------------
#include
#include
#include
#include

int main(void)
{
pid_t pid;
int n=0;

while ( n <= 20 )
{
pid = fork();
if( pid == -1 ) { perror("fork:"); }
n++;
}
sleep(60);
exit(0);
}
----------------------------------------
Compile
$ gcc -Wall ./fork-bomb.c -o ./bomb

For checking this, run the following command in another terminal
$ top -d 1 // one, not ell.

Then go back to the other terminal and run the program
$ ./bomb

Did it affect normal working of your machine ? ( Y / N )

Feedback:
Please point out mistakes and suggest which portions should
be removed, added, expanded etc etc.