I am trying to implement a custom Unix shell, however, the last command being passed to it is never recognized. When I run a "ls" nothing happens but if i do "ls ls" it acts as if only one ls has been passed, or if i do "ls -o" it only does "ls" but if i do "ls -o -o" it does "ls -o", it should function similarly to a standard Unix shell.
#include "apue.h"
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int parse(char* buf, char **cmds);
static void sig_int(int);
int
main(void)
{
char *path;
path = getenv("PATH");
char buf[MAXLINE];
char *envp[] = {path, NULL};
pid_t pid;
int status;
char *command[MAXLINE];
char *input;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal error");
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
parse(buf, command);
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
input = command[0];
execvpe(input, command, envp);
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if ((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
/*parses input into something runable*/
int parse(char* buf, char **cmds)
{
char *sep;
int count;
count = 0;
while((sep = strchr(buf, ' ')))
{
cmds[count]= buf;
*sep = '\0';
buf = sep +1;
count++;
while (*buf && (*buf == ' ')) /* Ignore spaces */
buf++;
}
cmds[count] = NULL;
if(count == 0)
return 1;
return 0;
}
Related
I'm trying to use pipe command and I can't understand how to.
I've a lot of versions but I can't make it work.
first of all the hierarchy:
main prog - nicecmp - that will execute the child prog and print the result
child prog - loopcmp - that will execute his child prog and get the returned value and send it back to the parent in nicecmp.
loopcmp's childs - lencmp/lexcmp - both prog will be executed in loopcmp and return value between -1 to 2. (100% works)
shortly, I need to create a pipe and a new process that will run new program (loopcmp - added in the end of the code) using execvp, and I need to print the res of the loopcmp in the parent.
I can send it directly from the prog that I executed and I can use WEXITSTATUS in the child after the end of the loopcmp.
what's the right way to do so (from the progrem execution or after that I've returned from the loopcmp)
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define LINELEN (80)
#define READFROM ("./loopcmp")
typedef enum { eLexcmp, eLencmp, eNumOfCmp } eCmpstr;
const char* cmpstr[eNumOfCmp] = { "./lexcmp", "./lencmp" };
int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);
int mygeti();
int main(int argc, char *argv[])
{
char str1[LINELEN + 1];
char str2[LINELEN + 1];
int index, rc, status, res;
int pfd[2];/* Pipe file descriptors */
if (pipe(pfd) == -1) /* Create pipe */
exit(-2); // pipe failed !
char* myargs[4];
myargs[0]=strdup(READFROM);
while (1)
{
printf("Please enter first string:\n");
if (mygets(str1, LINELEN) == NULL)
break;
printf("Please enter second string:\n");
if (mygets(str2, LINELEN) == NULL)
break;
myargs[2] = strdup(str1);
myargs[3] = strdup(str2);
do {
printf("Please choose:\n");
for (int i = 0; i < eNumOfCmp; i++)
printf("%d - %s\n", i, cmpstr[i]);
index = mygeti();
} while ((index < 0) || (index >= eNumOfCmp));
myargs[1] = strdup(cmpstr[index]);
rc = fork();
if (rc < 0) // fork failed !
{
printf("fork failed\n");
return -2;
}
else if (rc == 0) { // child !
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
execvp(myargs[0],myargs);
}
else { // parent
if (close(pfd[1]) == -1) /* Write end is unused */
exit(-2);
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
exit(-2);
if (close(pfd[0]) == -1)
exit(-2);
}
read(pfd[0], &res, sizeof(int));
printf("%d\n", res);
if (close(pfd[0]) == -1)
exit(-2);
}
}
return 0;
}
loopcmp ->
int main(int argc, char *argv[])
{
int status,rc,res = 0;
if (argc != 4)
{
return -1;
}
char* myargs[3];
for(int i=0;i<3;i++){
myargs[i]=argv[i+1];
}
rc = fork();
if (rc < 0) //fork failed
{
return -2;
}
else if (rc == 0) //I'm the child
{
if(execvp(myargs[1], myargs)==-1)
return -2;
}
else // parent
{
wait(&status);
res = WEXITSTATUS(status);
if(res ==254) // invalid file path ! (254== -2)
return -2 ;
}
write(fileno(stdout),&res,sizeof(int));
return res;
}
So I am trying to read from standard input and then get the input ready so that later on it can be used inside execvp().
What I am implementing here is basically a pipe for some terminal commands.
Here is how an example of my code goes.
input:
ls -s1
sort -n
output:
commands[0]="ls"
commands[1]="-s1"
commands2[0]="��""
commands2[1]="��""
sort: cannot read: t: No such file or directory
Here is my code
# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>
# include <string.h>
# include <sys/wait.h>
# define BUF_SIZE 256
int main()
{
char buffer[BUF_SIZE];
char *commands[5];
char *commands2[5];
int argc = 0;
int argc2 = 0;
fgets(buffer, BUF_SIZE, stdin);
for ( commands[argc] = strtok(buffer, " \t\n");
commands[argc] != NULL;
commands[++argc] = strtok(NULL, " \t\n") ) {
printf("commands[%d]=\"%s\"\n", argc, commands[argc]);
}
commands[argc] = NULL;
fgets(buffer, BUF_SIZE, stdin);
for ( commands2[argc2] = strtok(buffer, " \t\n");
commands2[argc2] != NULL;
commands2[++argc2] = strtok(NULL, " \t\n") ) {
printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc]);
}
commands2[argc2] = NULL;
int my_pipe[2];
if (pipe(my_pipe) == -1)
{
perror("cannot create pipe\n");
}
pid_t my_pid;
my_pid = fork();
if (my_pid < 0)
{
perror("Failed fork\n");
}
if (my_pid > 0)
{
close(my_pipe[1]);
dup2(my_pipe[0], 0);
close(my_pipe[0]);
wait(NULL);
execvp(commands2[0],commands2);
}
else
{
close(my_pipe[0]);
dup2(my_pipe[1], 1);
close(my_pipe[1]);
execvp(commands[0],commands);
}
}
One major problem is that you read the second line over the first in buffer, and the commands[] array contains pointers into buffer too. That's not a recipe for happiness. The simplest fix is to define char buffer2[BUF_SIZE]; and use that in the second fgets() call and for loop.
Using argc in printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc]); is a copy'n'paste bug — it should reference argc2 twice. This helped hide the previous problem.
Note that perror() does not exit; your code blunders on if pipe() fails, or if fork() fails.
The wait() in if (my_pid > 0) is bad; remove it.
If execvp() fails, you should report an error and exit with a non-zero status.
Putting those changes together yields code such as:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define BUF_SIZE 256
int main(void)
{
char buffer[BUF_SIZE];
char buffer2[BUF_SIZE];
char *commands[5];
char *commands2[5];
int argc = 0;
int argc2 = 0;
fgets(buffer, BUF_SIZE, stdin);
for (commands[argc] = strtok(buffer, " \t\n");
commands[argc] != NULL;
commands[++argc] = strtok(NULL, " \t\n"))
{
printf("commands[%d]=\"%s\"\n", argc, commands[argc]);
}
commands[argc] = NULL;
fgets(buffer2, BUF_SIZE, stdin);
for (commands2[argc2] = strtok(buffer2, " \t\n");
commands2[argc2] != NULL;
commands2[++argc2] = strtok(NULL, " \t\n"))
{
printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc2]);
}
commands2[argc2] = NULL;
int my_pipe[2];
if (pipe(my_pipe) == -1)
{
perror("cannot create pipe\n");
exit(EXIT_FAILURE);
}
pid_t my_pid = fork();
if (my_pid < 0)
{
perror("Failed fork\n");
exit(EXIT_FAILURE);
}
if (my_pid > 0)
{
close(my_pipe[1]);
dup2(my_pipe[0], 0);
close(my_pipe[0]);
execvp(commands2[0], commands2);
perror(commands2[0]);
exit(EXIT_FAILURE);
}
else
{
close(my_pipe[0]);
dup2(my_pipe[1], 1);
close(my_pipe[1]);
execvp(commands[0], commands);
perror(commands[0]);
exit(EXIT_FAILURE);
}
return EXIT_FAILURE;
}
When I run the program, it produces the appropriate output. Note that the return at the end of main() is actually never reached.
So I'm trying to create a custom shell for my school project. My method was to create child process, and have that process execute the command using the execvp() function that my professor briefly mentioned in class that we are meant to use. Here's my code, as always, any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#define MAX_LINE 80
int main(int argc, char *argv[])
{
char *input = (char*)malloc(MAX_LINE*sizeof(char));
int should_run = 1;
while(should_run){
printf("osh>");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0){
printf("error with creating chiled process");
return 0;
}
if(pid == 0){
fgets(input, MAX_LINE, stdin);
char *token = strtok(input," ");
if(execvp(token[0], token) < 0){
printf("Error in execution.");
return(0);
}
//should_run = 0;
}
waitpid(pid, 1, 0);
}
return 0;
}
The prototype of execvp is
int execvp(const char *file, char *const argv[]);
It expects a pointer to char as the first argument, and a NULL-terminated
pointer to an array of char*. You are passing completely wrong arguments.
You are passing a single char as first argument and a char* as the second.
Use execlp instead:
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
So
char *token = strtok(input," \n");
if(token == NULL)
{
fprintf(stderr, "only delimiters in line\n");
exit(1);
}
if(execlp(token, token, NULL) < 0){
fprintf(stderr, "Error in execution: %s\n", strerror(errno));
exit(1);
}
Also the convention in UNIX is to print error messages to stderr and a process with an error should
have an exit status other than 0.
As Pablo's states, you are passing the wrong arguments to execvp().
You can consider coding by yourself a function (char **strsplit(char *str, char delim)) which takes a string and split it into smaller pieces, returning an array of strings.
Also don't ignore compiler's warnings, they tell you a lot of things, and I suggest you to compile with gcc -Wall -Wextra -Werror to get almost any possible error in your program.
I tell you this because waitpid() takes as second argument a pointer to integer, to get an update of the status of the forked program. With this status you how the program exited (normally, segf, bus error...), you can use it to print an error if something went wrong.
You can consider using execv() instead (I know I'm going off topic, but you can learn useful things doing this), and find by yourself the correct executable(s).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#define MAX_LINE 255
char **strsplit(char *str, char delim);
char *strjoin(char const *s1, char const *s2);
int isexec(char *path)
{
struct stat buf;
lstat(path, &buf);
if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode))
return (1);
return (0);
}
static char *find_exec_readdir(char *paths, char *cmd)
{
DIR *dir;
struct dirent *dirent;
char *exec;
exec = NULL;
if ((dir = opendir(paths)) != NULL)
{
while ((dirent = readdir(dir)) != NULL)
{
if (!strcmp(dirent->d_name, cmd))
{
exec = strdup(dirent->d_name);
break ;
}
}
if (closedir(dir))
dprintf(2, "Failed closing dir.\n");
}
return (exec);
}
char *find_exec(char *cmd, char **paths)
{
char *exec;
char *path;
char *tmp;
int i;
i = -1;
exec = NULL;
path = NULL;
if ((cmd[0] == '.' || cmd[0] == '/'))
{
if (isexec(cmd))
return (strdup(cmd));
return (NULL);
}
while (paths[++i])
if ((exec = find_exec_readdir(paths[i], cmd)) != NULL)
{
tmp = strjoin(paths[i], "/");
path = strjoin(tmp, exec);
free(tmp);
free(exec);
break ;
}
return (path);
}
int handle_return_status(int status)
{
int sig;
int i;
if (!WIFEXITED(status) && WIFSIGNALED(status))
{
sig = WTERMSIG(status);
i = -1;
while (++i <= 13)
{
if (print_signal_error(sig))
{
return (-1);
}
}
dprintf(2, "Process terminated with unknown signal: %d\n", sig, NULL);
return (-1);
}
return (0);
}
int main(int argc, char *argv[])
{
char *input = NULL;
char **command = NULL;
int should_run = 1;
int status = 0;
(void)argc;
(void)argv;
if ((input = (char*)malloc(MAX_LINE*sizeof(char))) == NULL)
return (dprintf(2, "Failed to malloc, abort.\n"));
while(should_run){
printf("osh> ");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0)
return (dprintf(2, "error with creating chiled process\n"));
if(pid == 0){
fgets(input, MAX_LINE, stdin);
command = strsplit(input, ' ');
command[0] = find_exec(command[0], strsplit(getenv("PATH"), ':'));
if(execv(command[0], &command[1]) < 0)
return (dprintf(2, "Error in execution.\n"));
//should_run = 0;
}
waitpid(pid, &status, 0);
handle_ret_status(status);
}
return 0;
}
I'm trying to write a simple C shell. My problem is that I have written the program so that when the user enters a NULL value I've got the shell to exit and stop running. However after using different shells i've realised that the shell continues to loop. Is there anyway to fix this without having to rewrite my code? I'm still quite a novice to C.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_CMD_SIZE 512
int getPath(){
printf("PATH = %s\n", getenv("PATH"));
return 0;
}
int setPath(char* arg){
setenv("PATH", arg, 1);
return 0;
}
int setwd() {
char *arg;
arg = getenv("HOME");
chdir(arg);
return 0;
}
int main()
{
char buff[MAX_CMD_SIZE]; //buff used to hold commands the user will type in
char *defaultPath = getenv("PATH");
char *args[50] = {NULL};
char *arg;
int i;
pid_t pid;
setwd();
while(1){
printf(">");
if (fgets(buff, MAX_CMD_SIZE, stdin) == NULL) { //Will exit if no value is typed on pressing enter
setPath(defaultPath);
getPath();
exit(0);
}
arg = strtok(buff, " <|>\n\t");
i = 0;
if (arg == NULL) return -1;
while (arg != NULL){
printf("%s\n", arg);
args[i] = arg;
arg = strtok(NULL, " <|>\n\t");
i++;
}
if (strcmp(args[0], "exit") == 0 && !feof(stdin)){ //Will exit if input is equal to "exit" or ctrl + d
setPath(defaultPath);
getPath();
exit(0);
}
else {
pid = fork();
if (pid < 0){ //Error checking
fprintf(stderr, "Fork Failed\n");
} else if (pid == 0){ //This is the child procsess
execvp(args[0], args);
exit(-1);
} else { //Parent Process
wait(NULL); // Parent will wait for child to complete
}
}
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
void exec(char **args){
pid_t pid;
int status;
if ((pid = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0) {
if(execvp(args[0],args)<0)//{
//printf("argv[0]=%s argv[1]=%s",args[0],args[1]);
printf("**error in exec\n");
}
else {
while (wait(&status) != pid);
}
}
void exec2(char **args, char *file){
printf("file =%s\n",file);
int fd;
pid_t pid;
int status;
if ((pid = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0) {
fd = open(file, O_RDWR | O_CREAT, (mode_t)0600);
close(1);
dup2(fd, 1);
if(execvp(args[0],args)<0){
printf("**error in exec");
}
else {
printf("\nhere\n");
close(fd);
while (wait(&status) != pid){
fflush(stdout) ;
}
}
}
close (fd);
}
void main(){
char *command;
char inp[512];
char *filepath;
size_t size=0;
char *substr;
char *args[512];
command = (char *) malloc(sizeof(char *) * 512);
int flag=0;
int redirect=0;
int i=0;
while (1){
printf("$ ");
command = fgets(inp, 512/*sizeof(char *)*/, stdin);
command[strlen(command)-1]='\0';
if (strchr(command,'>')){
redirect=1;
strtok_r(command,">",&filepath);
}
size_t siz=4;
//printf("command=%s\n",command);
int i=0;
while(1){
//printf("i=%d\n",i);
char *tok = strtok_r(command," ",&substr);
if (tok==NULL){
break;
}
args[i++] = tok;
/* printf("tok=%s\n",tok);
printf("len tok = %d\n",(int)strlen(tok));
printf("command=%s\n",command);
printf("substr=%s\n",substr);
*/ command = substr;
}
//printf("args[0]=%s",args[0]);
if (!strncasecmp(args[0],"exit",siz) || !strncasecmp(args[0],"quit",siz))
{
printf("\nBye\n");
exit(0);
}
else if(strcmp(args[0],"cd")==0){
chdir(args[1]);
//printf("chdir") ;
//system("pwd");
}
else if (redirect==1){
exec2(args,filepath);
}
else exec(args);
}
}
Okay this is my code for my shell. When i run it, i put ls and it gives correct output. Then i put ls -l and then ls again and it gives
ls: cannot access : No such file or directory
Also when i use cd, ls doesnt give output and pwd says "ignoring unused arguments"
ALso cat doesnt work.
Though mkdir, ps and ls -l works.
Don't close stdout!
Do it like this, after the fork and before the exec:
if (child) {
int fd = open(file, O_RDWR | O_CREAT, (mode_t)0600);
close(1);
dup2(fd, 1);
if(execvp(args[0],args)<0){
printf("**error in exec");
}
}