Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Problem: There is a UNIX utility called, “at”. It is used to schedule a program

ID: 3639438 • Letter: P

Question

Problem: There is a UNIX utility called, “at”. It is used to schedule a program for delayed execution in situations when it may not be convenient for the user to interact with the system at odd hours of the day. See its man page. The purpose of this project is to develop a similar utility, which we name “At”. The syntax of “At” is as follows:
At time command-string
There are two ways to specify the time:
1)
@hh:mm:ss (exact time of execution today)
2)
+hh:mm:ss (time to lapse before execution)
As expected, 0 <= hh < 24, 0 <= mm, ss < 60.
The command-string is mostly a single shell command with its own options and arguments. Consider the following two examples to understand its use:
At +00:00:30 ping banana //Cause ping command to execute after 30 seconds
At @02:00:00 ls –i /tmp $HOME //execute ‘ls’ at 2 AM. If current time is already past 2 AM today, wait until 2 AM tomorrow.
If the command-string contains a shell meta-character or environment variable, it will be processed by the shell as usual at the time of execution of At, but not by At program. Hence, you may assume that the command-string is free of shell meta-characters.
This project is divided into two stages. The summary of each stage is as follows:
1.
Convert time argument to seconds, save the command-string, and find the executable file of the command.
2.
Create a new process, say At_cmd, which would sleep through the delay period and then execute the saved command string. At quits after creating At_cmd. This feature allows the user to log out, and yet be able to keep At_cmd alive for delayed execution of the submitted command.
It is a somewhat complex project, because its main goal is to teach a number of process management concepts as well as some C coding. A number of small C code files are stored in “~cs4352/tutorials” folder to help you with the C coding.
Each stage will be graded separately; “proj1.grading” file stored in “~cs4352/projects/proj1” contains grading details. Therefore, it is important that you implement features described for a stage in that stage only. The project development is further broken into a number of steps to improve testing.
Stage1: The purpose of this stage is to process the command line of At and save the command-string. You may assume that here is no ordering error, i.e., the time argument is given first and then the command-string, but the time argument itself may contain errors.
The outline of main() in stage1 is as follows:
1.
main( ) first calls a procedure, say process_arguments(argc, argv), to extract arguments.
2.
Next it calls another procedure, say delay = get_time_compute_delay(time)), to compute the delay period in seconds from the time extracted as a character string.
3.
Next it calls a library function pathfind(…) to determine the path of the executable file.
4.
Finally, it executes “sleep(delay)” to sleep through the assigned time, and executes system(“date”), i. e., in this stage, it is assumed that the given command is “date” for sake of simplicity. Execution of the actual command-string is taken up in Stage 2.
main ( )
{
Process arguments;
Compute delay;
Determine path of the executable of the command.
sleep (delay);
system (“date”);
}
Development of stage1 is further divided into following steps.
Step1a: process_arguments() processes argv[ ]. It copies time argument to a char string, and the rest of the command line as a list of strings, declared say as, “char **arglist”. For this purpose, it first counts the size of the command-string, uses malloc() to allocate an array of character pointers and then saves the arguments. For example, if the command line is “At +00:00:02 ls –l /home /tmp”, the net result should be that arglist[0]=”ls”,…, arglist[2] = “/home”, arglist[3] = “/tmp”, and arglist[3] = ‘’.
Output: Process_arguments( ) should print all items extracted from the command line such as below.
Time = +00:00:02
Arglist[0] = “ls”, arglist[1] = “-l”, …
Step1b: This step implements a procedure, “delay=get_time_compute_delay(time)” . It first checks the format of the ‘time’ argument. If it is well formed, it converts the ‘time’ into seconds and prints its value. Otherwise it prints an appropriate error message and returns -1 to the main().
Files “test_time.c” and “seconds.c” in tutorials folder contain some useful code to complete this procedure. “test_time.c” shows how to obtain the current time from “tm” structure and “seconds.c” shows how to convert the current time, obtained using “date” command, into seconds. (Caution – although function call, time(0) returns the number of seconds since mid-night, you need to compute it yourself, because one of the goals of this project is to do learn string processing.)
Caution: Do not declare just 2-byte space to store hh, mm, or ss of the time argument. The reason is that a string ends with ‘’. For example, if you declare
“char MM[2];” to store minutes component, set MM[0]=time[4] and MM[1]=time[5], and then do ‘atoi(MM)’ to convert the character representation of minute part to integer, you will not get the correct result, because ‘atoi()’ will not just process 2-byte MM, but keep scanning until it gets ‘’. Correct coding is: char MM[3]; MM[0]=time[4], …, MM[2]=’’, and then use atoi().
Test step1b using the following:
At +00:70:09 ls //error - minutes exceeded 59
At @00:1:25 ls //error - minutes should be given as 2-digit number
At &00:00:02 //error – time argument should begin with ‘+’ or ‘@’ only
At +01:10:12 man ls // correct input ; delay = 4012 seconds
At @00:00:02 man ls //correct command line;
Note that @time gives the actual time of execution of the command-string. For example, if @00:00:01, the stated time may already be passed today (unless you started At dead at the mid-night). In this case, you should compute the number of seconds to lapse until 00:00:01 of the next day.
Output: Print an appropriate error message or the delay period. For example, if you execute at 11:00:00 pm, the output for the fifth test should be delay=20801 seconds.
Step1c: This step verifies whether the command in the command-string is a valid command. It calls a library function, pathfind( ) to verify if an executable file corresponding to the command name exists on user’s PATH. Follow the little example given in the man page of pathfind ( ). Do not forget to include the header “libgen.h” and to use “-lgen” in the gcc command. At should print the pathname for debugging purpose.
Test step1c with the following:
At +00:00:03 LS // Bad command name
At +00:00:03 ping bigsun // correct command.
Output: Print the pathname or error message that command not found on PATH.
Stage1d: Add sleep() and system() to main() to execute “date” command.
Before submitting your programs, remove all debugging statements (not the print statements coded to show the output).
Stage2: The purpose of this stage is to create a child process, denoted as “At_cmd” for execution of the given command-string.
Step2a: Copy stage1.c to stage2.c and reorganize it as the following.
main ( )
{
Process arguments, compute delay, and verify path of the command.
if ((At_cmd_id = fork()) == 0) {print identities of the child; At_cmd (delay) } //child’s portion
else { print At_cmd_id; …} //parent’s portion
}
At_cmd (delay)
{ sleep (delay);
system (“date”);
}
In this step, At creates a child process, prints some information, and exits. It is the child process which calls At_cmd() to sleep through the “delay” period. It runs the same “date” command as in stage1. The logic of At_cmd( ) will evolve as you implement more features in later stages.
Note that the parent portion should not contain wait(0), because At is supposed to exit once the child is created.
When a user logins to a system, the login Shell opens three files, stdin, stdout, and stderr, and its children processes inherit them. Therefore, when At starts, these files are already opened and At_cmd inherits them from At. As a result, At_cmd would print the output of “date” on the same terminal (stdout), although the parent would have already completed. To verify this statement, as soon At completes, immediately run command, “ptree” before the delay period expires. You should see a line containing the process-id printed by the parent. It means that although At has completed, the child is not dead. If you like, you can kill the child process before it completes, by running “kill –9 At_cmd-id” (killing a process is not an offence).
Step2b: Purpose of this and the next step is to print a number of process identities. To determine what
At (the parent process) and At_cmd (the child process) portions should print, run my program for stage2. You would see that it prints output by giving appropriate labels such as “parent: At_cmd_id=” to tell what the output means and who printed it. Follow it to code the print statement portion.
Step2c: In step2a, the child process would die without executing “system()”, if you log out before the end of the delay period. The reason is that At_cmd belongs to the process hierarchy which is headed by the login shell. It is therefore necessary to separate the child process and make it the head of its own process group so that when the user logs off and the login shell dies, the child would survive on its own. Run “man –s 2 getpgrp” to learn about process group-id
To test this step, open two connections with the same UNIX host, run At command from one and close it. Run “ps-ef |grep At_cmd_id“ on the other connection to verify if the child is still alive.
Step2d: Irrespective of the time format, delayed execution should produce the same output as if the commands were executed immediately, i.e., there is no effect of delayed execution. Therefore, the environment at the time of execution should be the same that existed at the time of submission of the command-string. The purpose of this step is to verify whether the environment of a process changes after it is created. To do so, add getenv (“SHELL”); putenv (“SHELL=/usr/bin/sh”); in the parent’s portion, and getenv(“SHELL”); right after the sleep ( ) in the At_cmd( ). Label the output of two getenv( ) appropriately to show which of the two processes printed them.
Step2e: Finally, revise At_cmd( ) to complete stage 2.
1.
Pass pathname, the arglist, and the delay period as arguments to At_cmd().
2.
Replace system( ) by execv ( ) system call to run the given command_string.
Run the following to test stage2:
At +00:00:10 cal 2008
At +00:00:03 At +00:00:04 ping ficus //This command would not run unless “.” (current directory) is on your PATH. Edit your .bashrc and add “:.” at the end of line defining PATH. Does output of this command show two output of pathname, ./At and /usr/sbin/ping?
The logic step2e and step2a could be easily merged into one step, but the purpose is to emphasize that fork() and exec() are two separate process management concepts in UNIX..
Submission: Create a folder “proj1” in your “cs4352” folder. Develop a separate file for each stage in proj2 folder. Name the source and executable files of different stages appropriately such as At1.c and At1 for stage 1. Before submitting the project, go over proj2.grading file to verify if you implemented all items. Next remove all files except the source files and their executables from your “proj1” folder. Use the given script to submit proj1. Late completion penalty is 10% of your grade per day.
Demo programs: My executables are stored in the folder “~cs4352/projects/proj1”. Follow the readme file and run them.
/**********************************************************************************/

Explanation / Answer

Find the potential at the following points.

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote