Problems with ptrace(PTRACE_ME,...) and subsequent wait - linux

I am porting a debugger, 'pi' ('process inspector') to Linux and am
working on the code for fork/exec of a child to inspect it. I am
following standard procedure (I believe) but the wait is hanging.
'hang' is the procedure which does the work, the 'cmd' argument being
the name of the binary (a.out) to trace:
int Hostfunc::hang(char *cmd){
char *argv[10], *cp;
int i;
Localproc *p;
struct exec exec;
struct rlimit rlim;
i = strlen(cmd);
if (++i > sizeof(procbuffer)) {
i = sizeof(procbuffer) - 1;
procbuffer[i] = 0;
}
bcopy(cmd, procbuffer, i);
argv[0] = cp = procbuffer;
for(i = 1;;) {
while(*cp && *cp != ' ')
cp++;
if (!*cp) {
argv[i] = 0;
break;
} else {
*cp++ = 0;
while (*cp == ' ')
cp++;
if (*cp)
argv[i++] = cp;
}
}
hangpid = fork();
if (!hangpid){
int fd, nfiles = 20;
if(getrlimit(RLIMIT_NOFILE, &rlim))
nfiles = rlim.rlim_cur;
for( fd = 0; fd < nfiles; ++fd )
close(fd);
open("/dev/null", 2);
dup2(0, 1);
dup2(0, 2);
setpgid(0, 0);
ptrace(PTRACE_TRACEME, 0, 0, 0);
execvp(argv[0], argv);
exit(0);
}
if (hangpid < 0)
return 0;
p = new Localproc;
if (!p) {
kill(9, hangpid);
return 0;
}
p->sigmsk = sigmaskinit();
p->pid = hangpid;
if (!procwait(p, 0)) {
delete p;
return 0;
}
if (p->state.state == UNIX_BREAKED)
p->state.state = UNIX_HALTED;
p->opencnt = 0;
p->next = phead;
phead = p;
return hangpid;
}
I put the 'abort()' in to catch a non-zero return from ptrace,
but that is not happening. The call to 'raise' seems to be a
common practice but a cursory look at gdb's code reveals it is
not used there. In any case it makes no difference to the outcome.
`procwait' is as follows:
int Hostfunc::procwait(Localproc *p, int flag){
int tstat;
int cursig;
again:
if (p->pid != waitpid(p->pid, &tstat, (flag&WAIT_POLL)? WNOHANG: 0))
return 0;
if (flag & WAIT_DISCARD)
return 1;
if (WIFSTOPPED(tstat)) {
cursig = WSTOPSIG(tstat);
if (cursig == SIGSTOP)
p->state.state = UNIX_HALTED;
else if (cursig == SIGTRAP)
p->state.state = UNIX_BREAKED;
else {
if (p->state.state == UNIX_ACTIVE &&
!(p->sigmsk&bit(cursig))) {
ptrace(PTRACE_CONT, p->pid, 1, cursig, 0);
goto again;
}
else {
p->state.state = UNIX_PENDING;
p->state.code = cursig;
}
}
} else {
p->state.state = UNIX_ERRORED;
p->state.code = WEXITSTATUS(tstat) & 0xFFFF;
}
return 1;
}
The 'waitpid' in 'procwait' just hangs. If I run the program with
the above code, and run a 'ps', I can see that 'pi' has forked
but hasn't yet called exec, because the command line is still
'pi', and not the name of the binary I am forking. I discovered
that if I remove the 'raise', 'pi' still hangs but 'ps' now
shows that the forked program has the name of the binary being
examined, which suggests it has performed the exec.
So, as far as I can see, I am following documented procedures to
take control of a forked process but it isn't working.
Noel Hunt

I have found the problem (with my own code, as Nate pointed out), but the cause was obscure until I ran 'strace pi'. It was clear from that that there was a SIGCHLD handler, and it was executing a wait. The parent enters wait, SIGCHLD is delivered, the handler waits and thus reaping the status of the child, then wait is restarted in the parent and hangs because there is no longer any change of state. The SIGCHLD handler makes sense because the pi wants to be informed of state changes in the child. The first version of 'pi' I got working was a Solaris version, and it uses /proc for process control so there was no use of 'wait' to get child status, hence I didn't see this problem in the Solaris version.

Related

Implementing a custom command in a basic shell(in C)

I need to implement a custom command in a shell .The goal is to support a command program_name m that creates a child process to execute program_name, but aborts the process if it does not complete its operation in m seconds.
Actually I am checking the status of waitpid(child_pid,&exit_status,WUNTRACED) everytime ,but the code segment in the for loop is not working and is not aborting the process!!
Can anyone suggest me an appropriate routine from the library to deliver a SIGALRM signal after m seconds, and use a signal handler to perform appropriate actions?
Thanks in advance!!
int child_pid = fork();
if (child_pid < 0) {
perror("ERROR: could not fork process.\n");
}
if (child_pid == 0) {
char *args[4];
args[0] = path;
args[1] = "-c";
args[2] = command_line;
args[3] = (char *) 0;
//executing the command
int exec_return = execvp(path, args);
if (exec_return == -1) {
perror("error: command execution failed!\n");
} else {
exit(exec_return);
}
} else {
//assigning cur_fg_pid to the child
cur_fg_pid = child_pid + 1;
if (cur_fg_cmd == NULL) { cur_fg_cmd = (char *) malloc(sizeof(char)); }
strcpy(cur_fg_cmd, command_line);
int exit_status;
while (can_wait == 1 && waitpid(child_pid, &exit_status, 0) > 0) {}
cur_fg_pid = -1;
cur_fg_cmd = NULL;
if (can_wait == 1) latest_jobid = -1;
if(global_time_flag)
{
for (i = 0; i < stip_time; i++) {
if (waitpid(child_pid,&exit_status,WUNTRACED) > 0)
if (WIFSTOPPED(exit_status)) {
break;
} else {sleep(1);}
}
if(!WIFSTOPPED(exit_status ))
kill (child_pid, SIGUSR1);
}
}

How to synchronize input and output in pipes linux?

I am creating a shell command from the custom shell to do the ssh from one terminal to another terminal.
In order to do the ssh, I am using the inbuilt ssh command of the linux. Here is my code that does the ssh login.
However, I am seeing that the I/O buffers are not in sync.
This is what I am seeing on the terminal. After SSH to the other terminal. I did the following in the terminal.
PRT# ssh 192.168.10.42
PRT# Could not create directory '/root/.ssh'.
root#192.168.10.42's password:
# screen -r
-sh: cen-: not found
# hello
-sh: el: not found
#
I don't what's the reason here. Here is the code.
int sshLogin(chr *destIp)
{
char cmd[CMD_LEN];
char readbuff[CMD_LEN];
pid_t pid;
int ret = 0;
int fd[2];
int result;
memset(cmd,'\0',sizeof(cmd));
int status = 0;
/** --tt required to force pseudowire allocation because we are behind screen app **/
sprintf(cmd,"/usr/bin/ssh -tt %s",destIp);
/** create a pipe this will be shared on fork() **/
pipe(fd);
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0 )
{
/** Child Process of Main APP --Make this parent process for the command**/
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0)
{
/** basically Main APP grand child - this is where we running the command **/
ret = execlp("ssh", "ssh", "-tt", destIp, NULL);
printf("done execlp\r\n");
}
else
{
/** child of Main APP -- keep this blocked until the Main APP grand child is done with the job **/
while( (read(fd[0], readbuff, sizeof(readbuff))))
{
printf("%s",readbuff);
}
waitpid(0,&status,0);
LOG_STRING("SSH CONNC CLOSED");
exit(0);
}
}
else
{
/** Parent process APP MAIN-- **/
/** no need to wait let APP MAIN run -- **/
}
return 0;
}
Based on Patrick Ideas.
POST 2# - It seems that it works when we close the stdin in the parent process. However, it becomes very slugguish, I feel like I am typing the keyboard too slow. The system becomes too sluggish. Also, I have a web-server from this terminal. I see that I can no longer access the web.
So, the solution is somewhere around stdin but I am not sure.
int sshLogin(chr *destIp)
{
char cmd[CMD_LEN];
char readbuff[CMD_LEN];
pid_t pid;
int ret = 0;
int fd[2];
int result;
memset(cmd,'\0',sizeof(cmd));
int status = 0;
/** --tt required to force pseudowire allocation because we are behind screen app **/
sprintf(cmd,"/usr/bin/ssh -tt %s",destIp);
/** create a pipe this will be shared on fork() **/
pipe(fd);
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0 )
{
/** Child Process of Main APP --Make this parent process for the command**/
if((pid = fork()) == -1)
{
perror("fork:");
return -1;
}
if( pid == 0)
{
/** basically Main APP grand child - this is where we running the command **/
ret = execlp("ssh", "ssh", "-tt", destIp, NULL);
printf("done execlp\r\n");
}
else
{
/** child of Main APP -- keep this blocked until the Main APP grand child is done with the job **/
while( (read(fd[0], readbuff, sizeof(readbuff))))
{
printf("%s",readbuff);
}
waitpid(0,&status,0);
LOG_STRING("SSH CONNC CLOSED");
exit(0);
}
}
else
{
/** Parent process APP MAIN-- **/
/** no need to wait let APP MAIN run -- **/
close(stdin);
}
return 0;
}
Basically, I have added - close(stdin);
You have 2 different processes trying to read from STDIN. This causes process 1 to get char 1, process 2 to get char 2, process 1 to get char 3, process 2 to get char 4, etc, alternating back and forth.
Your 2 processes are:
execlp("ssh", "ssh", "-tt", destIp, NULL);.
while( (read(fd[0], readbuff, sizeof(readbuff))))
Basically you need to ditch the read(fd[0],...).
My initial thought is that perhaps it is buffering the output: stdout is buffered, so unless you print a newline, nothing will be printed until a certain number of characters build up. This is because I/O operations are expensive. You can find more detail on this here. The result is that there is a delay because your program is waiting to print.
My suggestion: in your main function, before calling your sshLogin function, try disabling buffering with this line of code:
setbuf(stdout, NULL);
You can also call fflush(stdout); periodically to do the same thing, but the above method is more efficient. Try it and see if that solves your problem.

Looping a fork()

This is a sample of my code for some piping I want to do. The problem is that pid2[0] does not supply me a child process. How do I fix it? pid2[1] and pid2[2] and so on do supply the parent with children.
int numCommands = numPipes + 1; /// not worrying about '>' and '<' right now
int *pipes = newint[numPipes*2]; /// two ends for each pipe
for(int i = 0; i < numPipes*2; i+=2) /// Offset by two since each pipe has two ends
pipe(pipes + i);
int *pid2 = new int [numCommands];
for(int i = 0; i < numCommands; i++)
{
pid2[i] = fork();
if(pid2[i] < 0)
{
std::cerr << "Failure to fork..." << std:endl;
return EXIT_FAILURE
}
if(pid2[i] == 0) /// Child process
{
if(i == 0) /// First Command
{
dup2(pipes[1], 1);
}
else if(i == numCommands -1) /// Last Command
{
dup2(pipes[2*(numCommands-1)-1], 0);
}
else /// Middle commands
{
dup2(pipes[2*(i-1)], 0);
dup2(pipes[(2*i)+1],1);
}
for(int j = 0; j < numPipes*2;j++)
close(pipes[j]);
execvp(pipeCommands[i][0], pipeCommands[i].data()); ///pipeCommands is a vector<vector<char*>>
perror("exec failed");
return EXIT_SUCCESS;
}
else /// The parent
{
for(int j = 0; j <numPipes*2;j++)
close(pipes[j]);
for(int k = 0; k < numCommands; k++)
waitpid(pid2[k],nullptr,0);
}
}
Your indexing into the pipes array is a little bit problematic. For sort file.txt | head | wc I'm assuming that numPipes is 2. Let's walk through your for loop for each value of i.
i==0
dup2(pipes[1], 1); // Send stdout to pipes[1]
i==1
dup2(pipes[2*(i-1)], 0); // dup2(pipes[0], 0), stdin from pipes[0]
dup2(pipes[(2*i)+1],1); // dup2(pipes[3], 1), stdout to pipes[3]
i == 2
dup2(pipes[2*(numCommands-1)-1], 0); //dup2(pipes[3], stdin from pipes[3]
In other words, any stdout from process 0 is going to a dead end. The second process will never read from its standard output. So the debug statement you put in there (cout == stdout, remember) will get lost as well.

UNIX Shell and History Feature

how to add History Feature in unix shell to allows the user to access the most recently entered
command , The user will be able to access up to 10 commands by using the
feature.
This comment explain the history part from the project:
The user will be able to access up to 10 commands by using the
feature. The commands will be consecutively numbered starting at 1, and
the numbering will continue past 10. For example, if the user has entered 35
commands, the 10 most recent commands will be numbered 26 to 35.
The userwill be able to list the command history by entering the command
history
at the osh> prompt. As an example, assume that the history consists of the
commands (from most to least recent):
ps, ls -l, top, cal, who, date
The command history will output:
6 ps
5 ls -l
4 top
3 cal
2 who
1 date
Your program should support two techniques for retrieving commands
from the command history:
1. When the user enters !!, the most recent command in the history is
executed.
2. When the user enters a single ! followed by an integer N, the Nth
command in the history is executed.
this is my code that including the history part, but i have error and dont know how to fix it. please help
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_LINE 80
char *history[10][MAX_LINE];
int po;
void setup(char inputBuffer[], char *args[],int *background)
{
int length,
i,
start,
ct;
ct = 0;
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
exit(0);
if (length < 0){
perror("error ");
exit(-1);
}
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' :
if(start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
start = -1;
break;
case '\n':
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL;
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default :
if (start == -1)
start = i;
}
}
args[ct] = NULL;
}
int main(void)
{
char inputBuffer[MAX_LINE];
int background;
char *args[MAX_LINE/2+1];
while (1){
background = 0;
printf("os>");
fflush(0);
setup(inputBuffer, args, &background);
/**
* After reading user input, the steps are:
* (1) fork a child process using fork()
* (2) the child process will invoke execvp()
* (3) if command included &, parent will invoke wait()
*/
pid_t pid = fork();
printf("Fork created.\n");
/*
For example, if the
user enters the command ps -ael at the osh> prompt, the values stored in the
args array are:
args[0] = "ps"
args[1] = "-ael"
args[2] = NULL
This args array will be passed to the execvp() function, which has the
following prototype:
execvp(char *command, char *params[]);
*/
if(pid < 0){
printf("Fork failed.\n");
}else if(pid == 0){
if( strcmp(args[0],"history") == 0){ /* Print History */
displayHistory();
}else if(strcmp(args[0],"r") == 0){ /* r num */
int index = (int) args[1];
/*runHistoryAt( index - 1);*/
}else if(strcmp(args[0],"rr") == 0){ /* Run recent */
/*runHistoryAt(0);*/
}else{ /* Execute normally */
printf("executing..., adding to history buffer\n");
/* Add args to history buffer */
int j;
for (j = 0; j < sizeof(args); j++) {
history[po][j] = args[j];
}
po = (po + 1) % 10;
/* Execute! */
execvp(args[0],args);
}
}
if(background == 0){
wait(NULL);
}else{
setup(inputBuffer, args, &background);
}
}
}
I would use the GNU readline library. It gives you line edition and history support and you can also have completion.

POSIX semaphore with related processes running threads

I have an assignment to implement Producer consumer problem in a convoluted way(may be to test my understanding). The parent process should set up a shared memory. The unnamed semaphores(for empty count and filled count) should be initialized and a mutex should be initialized. Then two child processes are created, a producer child and a consumer child. Each child process should create a new thread which should do the job.
PS: I have read that the semaphore's should be kept in a shared memory as they would be shared by different processes.
Please provide some hints, or suggest changes.
So far, I have done this:
struct shmarea
{
unsigned short int read;
unsigned short int max_size;
char scratch[3][50];
unsigned short int write;
sem_t sem1;// Empty slot semaphore
sem_t sem2;// Filled slot Semaphore
};
void *thread_read(void* args);
void *thread_write(void *args);
pthread_mutex_t work_mutex;
struct shmarea *shma;
int main()
{
int fork_value,i=0,shmid;
printf("Parent process id is %d\n\n",getpid());
int res1,res2;
key_t key;
char *path = "/tmp";
int id = 'S';
key = ftok(path, id);
shmid = shmget(key,getpagesize(),IPC_CREAT|0666);
printf("Parent:Shared Memory id = %d\n",id);
shma = shmat(shmid,0,0);
shma->read = 0;
shma->max_size = 3;
shma->write = 0;
pthread_t a_thread;
pthread_t b_thread;
void *thread_result1,*thread_result2;
res1 = sem_init(&(shma->sem1),1,3);//Initializing empty slot sempahore
res2 = sem_init(&(shma->sem2),1,0);//Initializing filled slot sempahore
res1 = pthread_mutex_init(&work_mutex,NULL);
while(i<2)
{
fork_value = fork();
if(fork_value > 0)
{
i++;
}
if(fork_value == 0)
{
if(i==0)
{
printf("***0***\n");
//sem_t sem1temp = shma->sem1;
char ch;int res;
res= pthread_create(&a_thread,NULL,thread_write,NULL);
}
if(i==1)
{
printf("***1***\n");
//sem_t sem2temp = shma->sem2;
int res;
char ch;
res= pthread_create(&b_thread,NULL,thread_read,NULL);
}
}
}
int wait_V,status;
res1 = pthread_join(a_thread,&thread_result1);
res2 = pthread_join(b_thread,&thread_result2);
}
void *thread_read(void *args)
{
while(1)
{
sem_wait(&(shma->sem2));
pthread_mutex_lock(&work_mutex);
printf("The buf read from consumer:%s\n",shma->scratch[shma->read]);
shma->read = (shma->read+1)%shma->max_size;
pthread_mutex_unlock(&work_mutex);
sem_post(&(shma->sem1));
}
}
void *thread_write(void *args)
{
char buf[50];
while(1)
{
sem_wait(&(shma->sem1));
pthread_mutex_lock(&work_mutex);
read(STDIN_FILENO,buf,sizeof(buf));
strcpy(shma->scratch[shma->write],buf);
shma->write = (shma->write+1)%shma->max_size;
pthread_mutex_unlock(&work_mutex);
sem_post(&(shma->sem2));
}
}
(1) Your biggest problem by far is that you have managed to write a fork bomb. Because you don't exit either child in the fork loop each child is going to fall through and loop around and create their own children until you crash or bring the system down. You want something more like this:
while(i < 2)
{
fork_value = fork();
if(fork_value > 0)
i++;
if(fork_value == 0)
{
if(i==0)
{
printf("0 child is pid %d\n", getpid());
int res;
res = pthread_create(&a_thread,NULL,thread_write,NULL);
res = pthread_join(a_thread,&thread_result1);
exit(0);
}
if(i==1)
{
printf("1 child is pid %d\n", getpid());
int res;
res = pthread_create(&b_thread,NULL,thread_read,NULL);
res = pthread_join(b_thread,&thread_result2);
exit(0);
}
}
}
for (i = 0; i < 2; ++i)
wait(NULL);
Notice the wait on the children which you neglected.
(2) Always check your return codes. They are like safety belts, a bit of a drag but so helpful when you crash. (Yes, I didn't take my advice here but you should.)
(3) These names are awful.
unsigned short int read;
unsigned short int write;
Stay away from naming variables after system calls. It's confusing and just asking for trouble.
(4) Terminology wise, processes with a common ancestor, like these, are related. The parent can open shared memory and other resources and pass it on to the children. Unrelated processes would, for example, multiple instances of program launched from different terminals. They can share resources but not in the "inherited" way forked processes do.
It's late and didn't get around to looking at what you are doing with the threads and such but this should get you started.

Resources