Redirecting stderr in C - c

I'm writing a simple shell in C and encountered a minor problem.
I have the following function:
int execStdErr(char** parsedArguments, int numberOfArgs) {
fflush(stderr);
int fd;
int parsedCommandLength = 0;
char** parsedCommand = parseLine(parsedArguments[0], &parsedCommandLength);
parsedArguments[numberOfArgs - 1] = deleteSpaces(parsedArguments[numberOfArgs - 1]);
if (fork() == 0) {
if (fd = open(parsedArguments[numberOfArgs - 1], O_WRONLY | O_CREAT |O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
perror("lsh");
return 1;
}
if (dup2(fd, 2) < 0) {
perror("lsh") ;
return 1;
}
close(fd);
execvp(parsedCommand[0], parsedCommand);
exit(0);
}
close(fd);
wait(NULL);
return 0;
}
parsedArguments are arguments splitted by 2>, then I take the last one as it is name of my file, and I process the previous one by splitting them on spaces (and they are in parsedCommand). For some reason the stderr prints on screen, it creates a file if it didn't exist but it is always empty. I don't know what might be the problem here.

A common error:
if (fd = open(...) < 0)
is equivalent to
if (fd = (open(...) < 0))
which is not what you want. You need:
if ( (fd = open(...)) < 0)
When open succeeds, open(...) < 0 evaluates to false and fd = open(...) < 0 assigns 0 to fd. The value returned by open, however, is lost.

Related

Why i cannot read from file thought it contains content?

I'm trying to create a function that duplicates a file given a file descriptor and a file name:
int filedup(int fd1, char *cpyfile)
{
int fd;
size_t rd;
char buff;
if (fd1 < 0 || fd1 > OPEN_MAX)
return (-1);
if (!validfname(fname))
return (-1);
fd = open(cpyfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
return (-1);
rd = read(fd1, &buff, 1);
while (rd > 0)
{
write(fd, &buff, 1);
rd = read(fd1, &buff, 1);
}
close(fd);
return (0);
}
int main(void)
{
int fd;
fd = open("/tmp/cpyfromfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
return (-1);
putstr_fd(strdup("hello world\n"), fd);
filedup(fd, "cpyfile");
close(fd);
return (0);
}
I tried to debug it, and the problem was rd == 0 even though the file contains data.
$ cat ./cpyfile
$ (nothing)
I'm not sure what's the problem ? what am i doing wrong ?

Copying file fails, EBADF on closing output file descriptor

So I was following a little outdated book (2010) and I'm trying to copy a file with Linux system calls. This is what i have:
NOTE: Ignore the tlpi_hdr.h and error_functions.h, they define errExit() and fatal() and some otheres, they just print the error and exit.
#include <stdio.h>
#include <fcntl.h>
#include "lib/tlpi_hdr.h"
#include "lib/error_functions.h"
#ifndef BUF_SIZE
#define BUF_SIZE 1024
#endif
int main(int argc, char *argv[])
{
int inputFd, outputFd, openFlags;
mode_t filePerms;
ssize_t numRead;
char buf[BUF_SIZE];
if (argc != 3 || strcmp(argv[1], "--help") == 0) {
usageErr("%s old-file new-file\n", argv[0]);
}
inputFd = open(argv[1], O_RDONLY);
if (inputFd == -1) {
errExit("Opening file %s", argv[1]);
}
openFlags = O_CREAT | O_WRONLY | O_TRUNC;
filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
outputFd = open(argv[2], openFlags, filePerms);
if (outputFd == -1) {
errExit("Opening file for writing %s", argv[1]);
}
while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0) {
if (write(outputFd, buf, numRead) != numRead))
fatal("I/O Error");
if (numRead == -1)
fatal("Reading error");
}
if (close(outputFd == -1))
errExit("close input");
if (close(inputFd == -1))
errExit("close output");
return EXIT_SUCCESS;
}
I'm failing on closing of the output file descriptor with EBADF Bad file descriptor:
thinkpad :: ~/.tlpi % ./cp.o a b
ERROR [EBADF Bad file descriptor] close output
The file copies fine tho:
thinkpad :: ~/.tlpi % sha1sum a
40a925a93e149ac53d2630cde8adeb63b8134b29 a
thinkpad :: ~/.tlpi % sha1sum b
40a925a93e149ac53d2630cde8adeb63b8134b29 b
thinkpad :: ~/.tlpi %
Why?
Let's take a closer look at your close call:
close(outputFd == -1)
Here you are comparing outputFd to the value -1. The result of that is a boolean value, which in C will be either 0 or 1. This happens to be either standard input or standard output, depending on the result. Not a file you descriptor you should close.
My guess is that you meant
if (close(outputFd) == -1)

Redirecting input and output in a shell

Hi I've been programming a shell in c and I got stuck while trying to redirect. While redirecting the stdout in my program works the stdin doesn't.
void redirect(node_t* node){
// mode 0: >$%d mode 1: < mode 2: > mode 3: >>
int input = 0;
if(node->redirect.mode == 2){
input = 1; // >
} else{
input = 0; // <
}
int pid = 0;
int *status = 0;
char * filename = node->redirect.target; // filename
int fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
if((pid = fork()) == 0){
dup2(fd, input); // STDIN OR STDOUT
close(fd);
node_t* node2 = node->redirect.child;
execvp(node2->command.program, node2->command.argv); // execute program
printf("failed to execvp\n");
exit(1);
} else {
wait(status);
}
}
I'm new to the fork() but my question is what am I doing wrong here that redirecting stdout works but stdin it writes nothing to the given file.
As mentioned in the comments, you need to use different open options depending on whether you're opening the file for input or output redirection. You can put this into your if.
int flags;
if(node->redirect.mode == 2){
input = 1; // >
flags = O_WRONLY | O_CREAT | O_TRUNC;
} else{
input = 0; // <
flags = O_RDONLY;
}
int pid = 0;
int *status = 0;
char * filename = node->redirect.target; // filename
int fd;
fd = open(filename, flags, 0666);
Also, you need to specify the permission modes for the case where the output file is created. It's OK to specify this argument all the time, it will be ignored when O_CREAT isn't in the flags.

Implementation of Redirection in User-Created Shell is not working properly

I am trying to create a shell similar to bash, with redirection and pipes.
In my main(), I call a parser and then call the function below. The problem I am running into is the fact that when I run my shell, it outputs to the terminal correctly, but it does not output to the file correctly when using the >, 1>, or 2>.
For instance, if I call:
pwd > foo5.txt
I end up receiving:
>
foo5.txt
In the text file I write too as opposed to the stdout (for ">"/"1>") or stderr (for "2>") which I am trying to achieve.
This is my code to fork and create the child process:
pid_t create_process(char *part, int const pipes[][2], int pipenum)
{
pid_t pid; // Initialize variables/pointers/arrays.
char *args[64];
int argc=0, n;
char *arg=strtok(part, " \t");
//char const **filename = args;
while(arg != NULL)
{
args[argc++]=arg;
arg=strtok(NULL, " \t");
}
args[argc++]=NULL;
pid = fork(); // Create Fork.
if(pid == 0)
{
int m;
if(pipes[pipenum][STDIN_FILENO] >= 0)
dup2(pipes[pipenum][STDIN_FILENO], STDIN_FILENO); // FD 0.
if(pipes[pipenum][STDOUT_FILENO] >= 0)
dup2(pipes[pipenum][STDOUT_FILENO], STDOUT_FILENO); // FD 1.
// Close all pipes.
for(m=0; m<64; m++)
{
if(pipes[m][STDIN_FILENO] >= 0)
close(pipes[m][STDIN_FILENO]);
if(pipes[m][STDOUT_FILENO] >= 0)
close(pipes[m][STDOUT_FILENO]);
}
char *filename;
char *newargs[64];
newargs[63] = NULL;
int i = 0;
int j = 0;
for(i = 0; i<64; i++)
{
if (args[i] == ">")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 1);
close(redir);
}
}
else if (args[i] == "2>")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 2);
close(redir);
}
}
else if (args[i] == "2>")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 2);
close(redir);
}
}
else if (args[i] == 0)
{
break;
}
else
{
newargs[j] = args[i];
j++;
cout<<"The arg is: " << newargs[j] <<endl;
}
}
execvp(newargs[0], newargs);
fprintf(stderr, "Command not found.\n");
exit(255);
}
else if(pid < 0)
{ // Error checking.
fprintf(stderr, "Fork Failed\n");
}
return(pid);
}
UPDATE: Now my code will not recognize the commands, and the arguments being printed (for error checking) appear as such:
ls > foo5.txt
The arg is: fprintf
The arg is:
The arg is: ▒
Command not found.
I see a few problems here:
First, you have the for loop where you scan the command arguments for redirection syntax (if (strcmp(args[i],">")==0) and so on) - but if the condition is true (meaning you found a redirection character) you're always opening args[2], not args[i+1].
Second (and this is why the redirection syntax gets passed on to the command you're running as command arguments) - once you detect redirection syntax, you don't remove the redirection operator or the target filename from the list of arguments that you pass to execvp().
For instance, if args[] = {"echo", "a", ">", "logfile", 0}, your code detects a request to send the output to a new file called "logfile" and redirects the FDs correctly, but it still passes those three arguments ["a", ">", "logfile"] to the command.
Third - in your loop, you're calling execvp() at the end of each conditional statement - meaning that you don't get to the end of argument processing before you launch the new process. You need to process all the command arguments for shell syntax and then exec the command.
To fix the various problems with arg handling, probably the most effective solution is to build a new argument list as you're processing the raw ones provided by the user. For instance (using a==b as a string equality test for brevity)
if (args[i] == ">")
{
i++; //skip the arg
if (args[i]) { // check we haven't hit the end of the arg list
filename = args[i];
// then open the file, dup it to stdout or whatever, etc...
}
else if (args[i] == "<") // repeat for other redirection syntax...
else { // Finally, handle the case where we didn't identify any shell syntax:
newargs[j] = args[i]; // Copy the arg to the new list, since it isn't "special"
j++; // Size of newargs[] has been increased
}
Note that this still doesn't handle things like no whitespace around the ">" character: "echo foo>file" will just print "foo>file"... Syntax processing gets a little more complicated in that case, as you've got to account for quoting rules and escape characters to process the arguments correctly.

File Descriptors, open() returns zero

i open a file and want to write something in it. The problem is that the fd2 for some reason is 0. Instead of writting in the file, it writes on terminal. I dont close(0) anywhere in my code. Why do i get fd = 0 and not for example 3. The reason that writes on terminal is that the value of fd is zero? I know that fd = 0 is for standard input,
Any Ideas? Thank you.
if ((fd2 = open(logFile, O_RDWR |O_APPEND | O_CREAT , 0666) == -1))
DieWithError("open() failed");
printf("FD2 = %d",fd2); //returns me zero
bzero(tempStr, sizeof(tempStr));
bzero(hostname, sizeof(hostname));
gethostname(hostname, sizeof(hostname));
sprintf(tempStr, "\n%sStarting FTP Server on host %s in port %d\n", ctime(&currentime), hostname, port);
if (write(fd2, tempStr, strlen(tempStr)) == -1)
DieWithError("write(): failed");
Your conditional is off. Mind the parentheses. It should be:
if ((fd2 = open(logFile, O_RDWR |O_APPEND | O_CREAT , 0666)) == -1)
// ^^^ ^^^
Sometimes it might be best not to outsmart yourself:
int fd = open(...);
if (fd == -1) { DieWithError(); }
This is wrong.
if ((fd2 = open(logFile, O_RDWR |O_APPEND | O_CREAT , 0666) == -1))
You want this.
if ((fd2 = open(logFile, O_RDWR |O_APPEND | O_CREAT , 0666)) == -1)
It's hard to see because the line is so long, but the parentheses are in the wrong place. In short,
if (( fd2 = open(...) == -1 )) // your code
if (( fd2 = (open(...) == -1) )) // equivalent code
if (( (fd2 = open(...)) == -1) )) // correct code
If the line is so long, best to keep it out of the if...
#include <err.h>
fd2 = open(...);
if (fd2 < 0)
err(1, "open failed");

Resources