I'm currently doing my Homework for UNIX(LINUX) programming.
I was assigned to build my own custom shell that all commonly used linux command and custom program can work.
I also created my_ls, my_cp, my_rm, my_cd for checking that both linux command and my own command works.
Simple story's are below
./myOwnShell // Run my own shell
home/testFolder>>ls . // Shell prompt
a.out helloWorld.txt myOwnShell.c myOwnShell // Print ls command's result
home/test/Folder>>my_ls . // Run my own ls command program
a.out helloWorld.txt myOwnShell.c myOwnShell
So far, all the linux command (which in /bin/) and my own command (which in home//bin/) works.
But it comes with differences when I type cd and my_cd - which changes current cwd
home/testFolder>>cd ..
Fail to run program // Error message from exec function failure
home/testFolder>>my_cd ..
// No message but also cwd is not changed
home/testFolder>> // Prompt from same folder
Somewhat pseudo source codes are below for myShell program
(I cannot copy/paste for my source cause it is in university server and transfer protocols are blocked)
int main() {
char** res; // store command by tokening
while (1) {
printf("%s>>", cwd);
gets(in); // get command
// <Some codes that split `in` by space and store it into res>
// <If cmd is "ls ./folder" -> res = ["ls", "./folder", NULL]>
pid = fork();
if (pid == 0) { // child
if (execvp(res[0], res) == -1) { // Run 'ls' command
printf("Fail to run program");
exit(0); // Exit child process
}
} else { // Parent, I omit in case of fork failure
wait(0);
// Omit exit status checking code
}
}
return 0;
}
Command cd is linux built-in command,
Command my_cd is my own program which change it's cwd.
And I do know that changing child process's cwd cannot effect to parent process and that's why 'cd' dose not change my shell's cwd. And I found that cd command is not in /bin/, so I guess cdis coded inside linux shell.
How can I make it work?
For linux cd
For my own my_cd -- I don't have it's source code, only have program. It is from my professor.
My guess is that cd cannot be implemented unless it is coded in shell itself. But professor give me this homework and it can mean that it is possible.
Any idea please?
Related
I want to find the number of context switches of a program. I know there is a file /proc/PID/status which reports the number of context switches continuously while the program is running. However, after the process is finished that file is deleted, so I am not able to check the content.
For this reason, in the following C code, I am trying to copying that file before and after the region of interest.
int pid_num = getpid();
system("sudo cp /proc/$pid_num/status start.txt");
// do
system("sudo cp /proc/$pid_num/status finish.txt");
As I run the program, I get this message
cp: cannot stat '/proc//status': No such file or directory
It seems that $pid_num in the system function is not correct. What is the correct form then?
C uses format specifiers equivalent to more generic $var shell counterpart. You need to prepare the command-string before invoking system() call.
#define START_FILE "start.txt"
#define END_FILE "finish.txt"
#define MAX_CMD_BUFFSIZE 256 // adjust as necessary
char cmdbuf[MAX_CMD_BUFFSIZE];
pid_t pid_num = getpid();
snprintf(cmdbuf, sizeof(cmdbuf), "sudo cp -f /proc/%d/status %s", pid_num, START_FILE);
int cmd_status = system(cmdbuf);
// verify
snprintf(cmdbuf, sizeof(cmdbuf), "sudo cp -f /proc/%d/status %s", pid_num, END_FILE);
cmd_status = system(cmdbuf);
// verify
use -f command option to replace target file if it already exists.
Also, you need to run this program with an user with sudo privileges; which is inviting trouble. If process status file is available for every user for reading, check if you can drop sudo from command.
I'm writing a shell in C. The shell has internal and external commands. The external commands can be extended with an ampersand (&) so they run in the background.
When I type e.g program&, the program executes with no problem in the background, making the shell available while the program is executing.
But, the output of the program can get mixed with the normal output of the shell. And that's what I'm trying to fix, with no success.
Note: In the example above I talked about program as any other command. The program basically sleeps and then prints "Hello, World!". Also, "program" is in /bin/, which is my default directory of external commands.
sish> pwd
[current directory]
sish> program&
sish> [now if I don't type anything...] Hello, World!
[now the current command is being written here].
For example, in bash, this behavior is different, it seems that, if the user is "still" writing the command, the output of the background program is holding (or waiting), so the user can execute a program. Hence, not messing with the output.
I read a bit about signals, I tried to setup a signal handler that printed something on SIGCHLD but it also had the same behavior.
CODE:
while(1) {
int internal=0;
int background=0;
int redirect=0; // 1 - output ; 2 - input
// those variables are not important for the question
printf("sish> ");
fgets(command,BUFFER_SIZE,stdin);
}
... (some lines that are not important for the question)
child = fork(); // pid_t child -> global variable
if(!child) {
if(redirect==1) {
int fd = open(words[2],O_WRONLY|O_CREAT|O_TRUNC,0660);
dup2(fd,1);
execlp(words[0],words[0],NULL);
}
else if(redirect==2) {
int fd = open(words[2],O_RDWR);
dup2(fd,0);
execlp(words[0],words[0],NULL);
}
else {
if(execvp(bin_path,words)==-1) {
printf("Error! Does the program exist?\n");
}
}
}
NOTES:
- I KNOW I'M NOT CHECKING FOR ERRORS IN THE FORK, I WILL ADD THAT WHEN I SOLVE THIS BUG.
- I ALSO PRINTED THE STDERR WITH perror, I GOT NOTHING.
I expect this (e.g):
sish> pwd
[current directory]
sish> program&
sish> [I wait...] pwd
[current directory]
Hello, World!
I am trying to implement a container, and for that I create a process using the clone(2) system call with the appropriate flags:
if ((child_pid = clone(child_main, process_struct.Stack + process_struct.StackPtr,
CLONE_NEWCGROUP
|CLONE_NEWIPC
|CLONE_NEWNET
|CLONE_NEWNS
|CLONE_NEWPID
|CLONE_NEWUTS
|SIGCHLD, &process_struct, checkpoint)) == -1){
fprintf(stderr,"Failed...%m \n");
exit(EXIT_FAILURE);
}else{
fprintf(stderr,"Done\n");
waitpid(child_pid, NULL, 0);
}
inside child_main() I Change the host name for the process's namespace, also i set the mount namespace, I installed a Linux file system hierarchy on a partition like a normal Linux installation (I did that to create a clean file system image clean of my files and binaries) and then I set the propagation type to MS_UNBINDABLE, then I pivot_root(2) to change my process's root directory.
const int child_main(struct process *process_struct, int *checkpoint){
char c;
fprintf(stderr,"=> IPC setup...");
//double check the IPC
close(checkpoint[1]);
fprintf(stderr,"Done\n");
if ( sethostname(process_struct->Hostname,
strlen(process_struct->Hostname)) || mounting(process_struct)){
return -1;
}
// startup the IPC pipes
read(checkpoint[0], &c, 1);
if(execve("/bin/bash", (char*)0, NULL) == -1 ){
fprintf(stderr,"--> Launching process Failed %m\n");
return -1;
}
return 0;
}
The problem is that my system goes over the execve(2) and does not launch the /bin/bash and the program flows without errors. When I add system(2) statement before the execve(2) : system("ls"); it lists the appropriate file system and current working directory. Also when I change the execve(2) paramters to either:
execve("/bin/ls", (char*)0, NULL) or execve("/bin/pstree", (char*)0, NULL) or any other parameter it will return an error of: No such file or directory or A NULL argv[0] was passed through an exec system call, also when I strace my program at the execve(2) system call it gives: NULL, 0, NULL) = 17992
The error has nothing to do with the file system image, I have performed more tests and are as the follwoing, I used for my mount namespaces my system's filesystem not the one I installed on a partition and running /bin/bash doesn't still work, I created a simple C program and compiled it, and it ran fine so there is something wrong that prevent bin/bash from being executed, to further test these results I reused for my mount namespaces the file system from my I moved the same executable to the file system first under "/" and second under the same path
my main system path to the executable= /home/omar/docs/test.out
my mounted file system from the partition path to the executable= /home/omar/docs/test.out
since I wanted to check if the same path might have caused a confusion while adding to each executable a statment so can tell which path did my program take, and it worked fine without any problem and correctly as expected, so the problem is just that system essential commands will not work.
You need to pass a proper argv array to execve. And if you just want to pass on the current environment, use execv rather than execve.
char *argv[] = {"bash", NULL};
if(execv("/bin/bash", argv) == -1 ){
perror("execv");
return -1;
}
I'm tasked with programming the linux cd command in C. I thought this would be fairly trivial by using the chdir() method, but my directories aren't changing. What's interesting is that the return status of chdir() is 0, not -1, meaning chdir() has not failed. Here are the two cases where I'm using chdir():
1.
char *dir = getenv("HOME"); // Here dir equals the home environment.
int ret = chdir(dir);
printf("chdir returned %d.\n", ret);
ret returns 1.
2.
int ret = chdir(dir); // Here dir equals the user's input.
printf("chdir returned %d.\n", ret);
ret returns 1 if the directory exists in my path.
Am I using chdir() wrong? I can't seem to find an answer for this anywhere. Any help would be much appreciated.
chdir() changes the working directory of the calling process only.
So when you have code like ...
int main() {
// 1
chdir("/"); // error handling omitted for clarity
// 2
}
... and compile that to a program example and then run it in a shell:
$ pwd # 3
/home/sweet
$ ./example # 4
$ pwd # 5
/home/sweet
Then you have two processes in play,
the shell, which is where you entered pwd and ./example
./example, the process launched (by the shell) with your compile program.
chdir() is part of your compiled program, not the shell, thus it affects only the process with your program, not the shell.
So, at // 1 the working directory of your program (in above example run) is /home/sweet, but at // 2 it is / as specified in the chdir() call above. This doesn't affect the shell and the output of pwd # 5 though!
gcc version 5.3.0 20151204 (Ubuntu 5.3.0-3ubuntu1~14.04)
I read this and and I find this line:
int exit_status = system("gnome-terminal");
so when I add it to my code it only open a new terminal window (well that's what he was asking for) but my program runs in the old one.
is there any way to run my program in a new terminal window.
and also when the program finish executing, the terminal window get closed like I typed the exit command
system("gnome-terminal"); will run the given command, wait for it to exit, and then continue with your program. That's why your program continues to run in the current terminal window.
Rather than trying to do this in C, it probably makes more sense to write a shell script wrapper for your program, and use that script to launch your program in a new terminal window:
#!/bin/bash
gnome-terminal -e ./your-program-name your program arguments
Make the script executable (chmod +x script-name), and then you can run it just like you would a C program. You can even have it forward the arguments from the script to your actual program:
#!/bin/bash
gnome-terminal -e ./your-program-name "$#"
Note that rather than using gnome-terminal (which assumes the user has gnome installed), you can use the more neutral x-terminal-emulator command instead (see How can I make a script that opens terminal windows and executes commands in them?).
If you really want to do this from your C program, then I'd recommend doing something like this:
#include <stdio.h>
#include <stdlib.h>
char cmd[1024];
int main(int argc, char *argv[]){
// re-launch in new window, if needed
char *new_window_val = getenv("IN_NEW_WINDOW");
const char *user_arg = argc < 2 ? "" : argv[1];
if (!new_window_val || new_window_val[0] != '1') {
snprintf(cmd, sizeof(cmd), "gnome-terminal -e IN_NEW_WINDOW=1 %s %s", argv[0], user_arg);
printf("RELAUNCH! %s\n", cmd);
return system(cmd);
}
// do normal stuff
printf("User text: %s\n", argv[1]);
return 0;
}
Using an environment variable (IN_NEW_WINDOW in this case) to check if you've already launched in a new window should make it so that the new window only opens once. Note that the above code assumes a program with only one argument.
However, I still think using the wrapper script is a better solution.