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

Help in Unix in bash here is the apue.h file lock_reg((fd), F_SETLKW, F_RDLCK, (

ID: 3727207 • Letter: H

Question

Help in Unix in bash here is the apue.h file


lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len)
lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lock(fd, offset, whence, len)
lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len)
lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))

pid_t lock_test(int, int, off_t, int, off_t); /* {Prog locktest} */

#define is_read_lockable(fd, offset, whence, len)
(lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define is_write_lockable(fd, offset, whence, len)
(lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)

void err_msg(const char *, ...); /* {App misc_source} */
void err_dump(const char *, ...) __attribute__((noreturn));
void err_quit(const char *, ...) __attribute__((noreturn));
void err_cont(int, const char *, ...);
void err_exit(int, const char *, ...) __attribute__((noreturn));
void err_ret(const char *, ...);
void err_sys(const char *, ...) __attribute__((noreturn));

void log_msg(const char *, ...); /* {App misc_source} */
void log_open(const char *, int, int);
void log_quit(const char *, ...) __attribute__((noreturn));
void log_ret(const char *, ...);
void log_sys(const char *, ...) __attribute__((noreturn));
void log_exit(int, const char *, ...) __attribute__((noreturn));

void TELL_WAIT(void); /* parent/child from {Sec race_conditions} */
void TELL_PARENT(pid_t);
void TELL_CHILD(pid_t);
void WAIT_PARENT(void);
void WAIT_CHILD(void);

#endif /* _APUE_H */
  

Explain the logic of the program in Example 2, especially on (a) when SIGTSTP is received, which process (parent or child) is handling the signal, and (b) what happens (or is done) by the signal handler (signhand).

void sighand(int signum){

/* supposedly need to reset this here (per "man signal" and

   it's discussion of signals under linux), but doesn't actually

   seem to matter. Documentation != observed program behavior. */

kill(pid_child,SIGKILL);

fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);

}

(3) Modify the program in Example 2 so that (a) the signal is handled by the child (not the parent), and (b) output message (fprintf statement) in sighand for SIGTSTP will also print the PID of the current process handling the signal.

void sighand(int signum){

/* supposedly need to reset this here (per "man signal" and

   it's discussion of signals under linux), but doesn't actually

   seem to matter. Documentation != observed program behavior. */

kill(pid_child,SIGKILL);

fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);

}

EX 1:

#include "apue.h"

static void sig_usr(int);     /* one handler for both signals */

int

main(void)

{

if (signal(SIGUSR1, sig_usr) == SIG_ERR)

    err_sys("can’t catch SIGUSR1");

if (signal(SIGUSR2, sig_usr) == SIG_ERR)

    err_sys("can’t catch SIGUSR2");

for ( ; ; )

pause();

}

static void

sig_usr(int signo)       /* argument is signal number */

{

if (signo == SIGUSR1)

    printf("received SIGUSR1 ");

else if (signo == SIGUSR2)

    printf("received SIGUSR2 ");

else

          err_dump("received signal %d ", signo);

       }

Commands for EX 1:

$ ./a.out &

[1]      7216

$ kill -USR1 7216

received SIGUSR1

$ kill -USR2 7216

received SIGUSR2

$ kill 7216

[1]+ Terminated

./a.out

EX 2:

// minishell part 1 - Shell sample code with execvp

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <signal.h>

#define MAX_ARG 10

int pid_child;

/*** Main ***/

void sighand(int signum);

int main(){

    // declare variables

    char *prompt = "sh1$ ";

    char line[256];

    char line1[256];

    char program[32], command[32], *args[MAX_ARG];

    int pid, status, i;

    // print the prompt

    printf("%s", prompt);

    // Try getting input. If error or EOF, exit

    while(fgets(line, sizeof line, stdin) != NULL)

    {

        // fgets leaves ' ' in input buffer. ditch it

        line[strlen(line)-1] = '';

        strcpy(line1,line);

        // If the input is not null, get in

        if (line != NULL) {

           

            // parse command and arg

            args[0] = strtok(line, " "); // grab command

            for(i=1; i<10; i++)                 // grab arguments

                args[i] = strtok(NULL, " ");

            strcpy(command, args[0]);     // store command variable

            // check to see if the command is 'exit'

            if(!strcmp("exit", args[0])) {

                exit(0);

            }

            // check to see if the command is 'cd'

            else if(!strcmp("cd", args[0])) {

                chdir(args[1]);

            }

            // handles all other commands

            else {

               pid = fork(); // fork to create new child   

               if(pid == 0) { // code to be run by the child     

              signal(SIGTSTP, SIG_IGN);

                    status = execvp(command, args); // execute

                    exit(0);                   // exit child

                }

                else {       // code to be run by the parent

                    pid_child = pid;

                  signal(SIGTSTP, sighand);

                    waitpid(-1); // wait for children to finish

                  signal(SIGTSTP, SIG_DFL);

                }

            }

        }

      // print the prompt

    printf("%s", prompt );

    }

    return 0;

}

void sighand(int signum)

{

/* supposedly need to reset this here (per "man signal" and

   it's discussion of signals under linux), but doesn't actually

   seem to matter. Documentation != observed program behavior. */

//signal(signum, nyahnyah);

kill(pid_child,SIGKILL);

fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);

}

$ ./a.out &

[1]      7216

$ kill -USR1 7216

received SIGUSR1

$ kill -USR2 7216

received SIGUSR2

$ kill 7216

[1]+ Terminated

./a.out

Explanation / Answer

Explain the logic of the program in Example 2, especially on

(a) when SIGTSTP is received, which process (parent or child) is handling the signal ?

Ans.   Following are the contents from your code.

if(pid == 0) { // code to be run by the child    

              signal(SIGTSTP, SIG_IGN);

              status = execvp(command, args); // execute

              exit(0);                   // exit child

}

else {       // code to be run by the parent

pid_child = pid;

              signal(SIGTSTP, sighand);

              waitpid(-1); // wait for children to finish

              signal(SIGTSTP, SIG_DFL);

}

This signal function takes two arguments. First argument is the signal to be handled and the 2nd argument is the action to be done on the reciept of that signal. There are three actions can be done on the receipt of a signal.

SIG_DFL ----- Default action to be taken. These default actions are enabled for every signal and no need to call signal() function to enable it. The default actions for every signal is pre-defined and cannot be modified. The default action for the signal SIGTSTP is to suspend the process which receives the signal.

SIG_IGN ----- Signal has to be ignored. The action makes the process to ignore the signal upon receipt.

UserDefined ----- User can manually define a function with actions i.e. what to be done when a process receives a signal.

Note : fork() returns 0 to the child process and the child's process id to the parent.

so pid is 0 for child and pid = x for parent, when x is child's process id.

The parent block executes the following code.

pid_child = pid; // child_pid variable is updated with pid. i.e. child_pid now holds the process id of child.

signal(SIGTSTP, sighand); // we are making parent process to call sighand() when it received SIGTSTP. So basically parent is hadling the signal.

waitpid(-1); // wait for children to finish. This is not the correct syntax. You have to use wait(-1) or waitpid(child_pid, NULL, 0);

signal(SIGTSTP, SIG_DFL); // After killing the child, we are changing the action for the signal to default.

Explanation: When user enters CTRL+Z in place of any command, At this point, parent will receive SIGTSTP (because of ctrl+z), and it has to go to suspended state. So parent calls the signal handler to do the cleanup process. Parent does the cleaning by following code.

kill(pid_child,SIGKILL);

fprintf(stderr, "nyah, nyah Mr. #%d. ", signum); // we are printing some message with signal number to std error.

By sending signal SIGKILL to child process. This signal causes the child to terminate. But note that, here child pid doesn't hold any valid process id because the fork() has not been called when user pressed ctrl+z. So the variable child_pid is holding older child's process id (from the last command if executed), which had been killed by exit(0) after executing execvp(). So this is an invalid operation. Parent is trying to kill an invalid child process.

Whereas in child, we are executing

signal(SIGTSTP, SIG_IGN);

Which will cause the signal to be ignored upon receiption. The signal SIGTSTP can be given manually by user using ctrl+z, or when you try to do some invalid memory operations the os will try to suspend the process. Thus child will ignore the signal SIGTSTP.

(b) what happens (or is done) by the signal handler (signhand).

I think the answer to this question is explained in previous question answer. The parent is basically trying to do the cleanup by killing the child (by sending SIGKILL which causes the receiving process to be terminated). This case is only useful, when you have entered a command which takes much more time to execute and you dont want to wait, so you have given ctrl+z, then parent will try to terminate the program.

(3) Modify the program in Example 2 so that (a) the signal is handled by the child (not the parent), and (b) output message (fprintf statement) in sighand for SIGTSTP will also print the PID of the current process handling the signal.

If you want the child to handle this you can do this by using following code. Now we will call sighand in child. So when child receives SIGTSTP it will terminate itself. And if parent receives SIGTSTP, the default action is to terminate. So parent will terminate and the child will receive signal SIGHUP, whose default action is to terminate process. So child will automatically terminate if parent terminate.

// minishell part 1 - Shell sample code with execvp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_ARG 10

int pid_child;
/*** Main ***/
void sighand(int signum);
int main(){
    // declare variables
    char *prompt = "sh1$ ";
    char line[256];
    char line1[256];
    char program[32], command[32], *args[MAX_ARG];
    int pid, status, i;

    // print the prompt
    printf("%s", prompt);
    // Try getting input. If error or EOF, exit
    while(fgets(line, sizeof line, stdin) != NULL)
    {
        // fgets leaves ' ' in input buffer. ditch it
        line[strlen(line)-1] = '';
        strcpy(line1,line);
        // If the input is not null, get in
       // if (line != NULL) {
        if (strlen(line)) {
            // parse command and arg
            args[0] = strtok(line, " "); // grab command
            for(i=1; i<10; i++)                 // grab arguments
                args[i] = strtok(NULL, " ");
            strcpy(command, args[0]);     // store command variable
            // check to see if the command is 'exit'
            if(!strcmp("exit", args[0])) {
                exit(0);
            }
            // check to see if the command is 'cd'
            else if(!strcmp("cd", args[0])) {
                chdir(args[1]);
            }
            // handles all other commands
            else {
               pid = fork(); // fork to create new child
               if(pid == 0) { // code to be run by the child   
           signal(SIGTSTP, sighand);
           status = execvp(command, args); // execute
           exit(0);                   // exit child
                }
                else {       // code to be run by the parent
                    pid_child = pid;
                    waitpid(pid_child, NULL, 0); // wait for children to finish
                }
            }
        }

      // print the prompt
      printf("%s", prompt );
    }
    return 0;
}

void sighand(int signum)
{
/* supposedly need to reset this here (per "man signal" and
   it's discussion of signals under linux), but doesn't actually
   seem to matter. Documentation != observed program behavior. */
//signal(signum, nyahnyah);
// kill(pid_child,SIGKILL);
fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);
printf("Process %d is handling signal %d ", getpid(), signum);
kill(getpid(),SIGKILL); //As child itself is handling this, call getpid(), which will return its own pid and send SIGKILL to itself
}

Ping me back for doubts. Thanks.