CSc 3320: System Level Programming, homework 2

This homework is about how system/user information can be obtained via shell commands as well as C function.

Using C for system/user information

The system command allows you to call a shell function from C code. It returns an integer noting success/failure. The argument to it should be a string constant. You may be able to get around this, though it is not a good idea since it can introduce a security weakness.

Consider the following program.


    #include <stdio.h>
    #include <stdlib.h>     // defines "system"
    
    int main (int argc, char *argv[]) {
    
      int returnValue;  
    
      returnValue = system("echo hello world"); 
      printf("The command returned with value %d.\n", returnValue);
    
      return 0;
    }
Compiling it and running it shows:
    cascade:c++files> ./a.out
    hello world
    The command returned with value 0.
Notice that the program called the "echo" command, and the output went to the terminal. The C program does not "see" the output, only the return value.

Here is a bad example. Suppose that we issue the following command.

    returnValue = system("hello world");
The program will compile, but there is no shell command called "hello", so it will generate an error. The return value will be non-zero.

Now suppose that you compiled the "hello world" program (the one that uses printf) to an executable named "hello", and that it is in the current directory. The above command will still fail, however, if you change it to

    returnValue = system("./hello");
then it will work. That is, you can use system to call programs that you write yourself. The programs called could be other compiled programs, or could be shell scripts.

Getting Simple Process/User Information in C

The next example gets the ID number of the current process, followed by the ID number belonging to the parent of that process. "pid_t" is an integer type used for process IDs. You could use "int" instead, but it is good practice to use "pid_t".

    pid_t my_id;
    my_id = getpid();
    printf("syscall returned %d as the process id.\n", my_id);
    my_id = getppid();
    printf("syscall returned %d as the id of the parent process.\n", my_id);
See gnu.org page on process identification for more info.
Here are two example runs.
    cascade:c++files> ./a.out
    syscall returned 1373 as the process id.
    syscall returned 607 as the id of the parent process.
    cascade:c++files> ./a.out
    syscall returned 1374 as the process id.
    syscall returned 607 as the id of the parent process.
Notice how the ID of the parent process is the same in both, since the shell has not changed. The process ID changes, though, such as 1373 only lasted until the end of the run. On the next run, the new process is assigned the ID 1374.

Also worth noting is that "syscall" here simply means a system call. "syscall" is also the name of a deprecated function.

Use the following command to get the user ID:

    my_id = getuid();
The group ID can be found with the following.
    my_id = getgid();

The shell commands invoked by "system" create a new shell each time, meaning that it does not preserve the state of the former commamd. So if we do ls then cd then ls again, as three separate system commands, the results of the two ls commands will be the same. Consider the following code:

    printf("Calling the pwd command...\n");
    returnValue = system("pwd"); 
    printf("    return value is %d.\n", returnValue);
    printf("Now do \"cd /etc\" ...\n");
    returnValue = system("cd /etc"); 
    printf("    return value is %d.\n", returnValue);
    printf("Calling the pwd command...\n");
    returnValue = system("pwd"); 
    printf("    return value is %d.\n", returnValue);
When run, the output looks like this.
    Calling the pwd command...
    /Users/mweeks/programs/c++files
        return value is 0.
    Now do "cd /etc" ...
        return value is 0.
    Calling the pwd command...
    /Users/mweeks/programs/c++files
        return value is 0.
The pwd command gives the same result each time, even though the cd command changed the directory to "/etc", at least momentarily. That is, a new shell was created to handle the cd command, and it did change the directory. But then that shell terminated at the end of the system command, and the directory reverted back to what it was when that shell process was created.

What if you want to change the directory, in a way that lasts? There is a chdir command. Here is an example, replacing the system("cd /etc") line from the previous example with this following line.

    returnValue = chdir("/etc"); 
This time, we get the following output.
    Calling the pwd command...
    /Users/mweeks/programs/c++files
        return value is 0.
    Now do "chdir()" ...
        return value is 0.
    Calling the pwd command...
    /private/etc
        return value is 0.
Notice how the pwd command gives a different result the second time it is called. The "/etc" directory on this particular computer maps to "/private/etc". After the process terminates, the computer reverts to the original directory. That is, the chdir function only affects the current directory of the process.

The Sleep Function

The sleep function is sometimes necessary. As you might guess from the name, it causes the process to "sleep" for a specified amount of time. One such use of this function is to periodically check for some condition to change or event to occur. Yes, you could have the check in a while loop without the "sleep" function, however it will drag down the system's performance. Calling "sleep" means that other processes get the chance to run.

Next is a short example demonstrating the "sleep" function.

    #include <unistd.h>        // for sleep
Then later in the program:
    printf("Starting...\n");
    sleep(10);
    printf("Now it is 10 seconds later.\n");
When run, the process prints the "Starting..." message. Then the process "sleeps" for 10 seconds. After this, it prints the second message.

Getting Simple Process/User Information in a shell

A shell script can simply be a collection of commands that you have entered at the command prompt. For example, put the following into a text file. (Yes, you should do this. No, you do not have to turn it in.) It is good practice to name a shell script with an extension of ".sh", though you may see shell scripts with a ".bash" extension if they are specifically using Bash commands. Click here for an example bash script.


    echo "Calling pwd"
    pwd
    echo "done"
Use the chmod command to make the text file executable. Then, run it by typing "./" in the shell followed by the name, just like we use to run compiled C programs.

To find the parent process, use $PPID. For the current process, use $$.


    cascade:Desktop> echo $PPID
    562
    cascade:Desktop> echo $$
    570
Get the username by utilizing the $USER variable. This is set automatically; and you do not need to define it. Likewise, the user ID is stored in $UID.

    cascade:Desktop> echo "Hello $USER"
    Hello mweeks
    cascade:Desktop> echo $UID
    501
The group ID can be found with $GID.

    cascade:Desktop> echo $GID
    20
Next, we can call "sleep" and "ps" easily from the shell.

    cascade:Desktop> sleep 2
    cascade:Desktop> ps
      PID TTY           TIME CMD
      570 ttys000    0:00.10 -zsh
      572 ttys001    0:00.01 -zsh
      571 ttys002    0:00.01 -zsh
      595 ttys003    0:00.01 -zsh
    cascade:Desktop> 
Process 562 in this case is not shown, due to defaults with the process status (ps) command. To see all process information for a user, use "ps -U username". You do not need to use the "-U username" for this assignment.

Program Description

Here we will compare a couple of ways of doing things. You will create and turn in two programs: one is a C program, and the other is a shell script.

C programming Tasks

Start your program with the message "Starting C program". At the end of program, print the message "Ending C program".

Use getpid and getppid, and show the values of each of these along with an appropriate message saying what they are. Also get the group ID, and print this using an appropriate message.

Get the user ID. If the user ID matches your account, print a "Welcome your name" message, and then use "grep" with your user ID for the file /etc/passwd. You can hard-code your user ID into the grep string. If the user ID does not match your account, print the user ID using an appropriate message, but do not print the "Welcome" message and do not use "grep" on your user ID.

Note that the "system" call is a general-purpose function.

    returnValue = system("ps");
This example returns text, however, it goes to the terminal (or more specifially, to stdout). The calling program does not "see" the resulting output. In your C program, use this command:
    returnValue = system("ps");
and include an appropriate message before it. If you want more information, there is code showing system("ps"); in the Unix book, e.g. on page 482, but "system" does not appear to be in the index. This command (system) is in the C book on page 689.

In C, get a list of processes (with "ps" as a system command). Then call the sleep function for 3 seconds (with "sleep" as a system command). Get a list of processes again, and compare it to the first list of processes. Is the list of processes the same?

Shell Script programming Tasks

Next, create a shell script to give the same output as your C program, with following exceptions. "Starting C program" should be "Starting shell script". "Ending C program" should be "Ending shell script". The output from "ps" will be slightly different.

Also, you can do conditional statements in a shell script, but we will not do this here. Instead, use $USER within the welcome message, i.e. "Welcome $USER". Then use grep with the $USER variable for the file /etc/passwd. That is, the shell script version will look for the username in the /etc/passwd file regardless of who runs your program.

Turning this in

Refer to the previous homework's instructions for submitting your work. Be sure to turn in a log of activity showing successful compilation and runs. Also turn in a copy of each program that you write.


Added October 09, 2024, 08:53 AM:
There are a few things to note about this homework: