i've been trying to redirect the output to a file and reading from file instead of stdin by using a function, but it doesnt seem to be working as when i redirect into a file and check to see if a file has been created with the output there is nothing there. what could be wrong with this function.
/* Redirect a standard I/O file descriptor to a file
* Arguments: filename the file to/from which the standard I/O file
* descriptor should be redirected
* flags indicates whether the file should be opened for reading
* or writing
* destfd the standard I/O file descriptor which shall be
* redirected
* Returns: -1 on error, else destfd
*/
int redirect(char *filename, int flags, int destfd){
int ret;
if(flags == 0){
destfd = open(filename,O_RDONLY);
if (destfd < 0){
return -1;
}
ret = dup2(0,destfd);
if(ret < 0){
return -1;
}
close(destfd);
}
if(flags == 1){
destfd = open(filename,O_APPEND|O_WRONLY);
if (destfd < 0){
return -1;
}
ret = dup2(destfd,1);
if(ret < 0){
return -1;
}
close(destfd);
}
return destfd;
}
There are several problems with your code, not least of which the really awful formatting which make it very hard to read.
For example this call to dup2 is backwards - it's replacing the recently opened destfd with a copy of stdin.
ret = dup2(0,destfd);
a then a few lines later you close destfd.
Your if statements could benefit from you learning about else and else if
if(flags == 0) {
// code
} else if(flags == 1) {
// code
}
Really though you could simplify the whole function by treating the flags parameter as the same flags you'd pass to open and have destfd as the file descriptor you want to replace.
int redirect(char *filename,int flags, int destfd)
{
int fd;
fd=open(filename,flags,S_IRUSR|S_IWUSR);
if(fd!=-1)
{
if(dup2(fd,destfd)==-1)
{
close(fd);
fd=-1;
}
else
{
close(fd);
}
}
return fd;
}
Then you could call it like
redirect("output.txt",O_CREAT|O_WRONLY,1); // Redirect stdout
redirect("input.txt",O_RDONLY,0); // Redirect stdin
The os function dup2() should provide what you need (if not references to exactly what you need).
More specifically, you can dup2() the stdin file descriptor to another file descriptor, do other stuff with stdin, and then copy it back when you want.
The dup() function duplicates an open file descriptor. Specifically, it provides an alternate interface to the service provided by the fcntl() function using the F_DUPFD constant command value, with 0 for its third argument. The duplicated file descriptor shares any locks with the original.
On success, dup() returns a new file descriptor that has the following in common with the original:
Same open file (or pipe)
Same file pointer (both file descriptors share one file pointer)
Same access mode (read, write, or read/write)
Everything I said can be found on the manpage of dup
Related
Will EXECVP system call supports IO redirections
That means is this give desired output
char *args[]={"cat","A.txt","B.txt",">","C.txt",NULL};
execvp(args[0],args);
I mean will the data in A.txt and B.txt goes to C.txt
If no why ?
UPD : I have asked two doubts in comment please clarify it
This is technically not an answer to your question, that has been answered in the comments. But an explanation to how you can do redirection with execvp
When starting a new program with execvp, it will inherit the current file descriptor. So if you setup file descriptor 1 (which is used for stdout)
to be redirected to "C.txt" before calling execvp, the new program will
write to "C.txt":
// Open "C.txt" for writing, creating it if it doesn't exist, or
// clearing its content if it does exist.
// `mode` should be set as appropriate
//
int fd = creat("C.txt", mode);
if (fd == -1)
{
// Swap out error handling to suit your needs
perror("open failed");
exit(EXIT_FAILURE);
}
// We want new process to have "C.txt" on file descriptor 1
if (dup2(fd, 1) == -1)
{
perror("dup failed");
exit(EXIT_FAILURE);
}
// "C.txt" is now writable on file descriptor 1, so we don't need the
// old one. Actually, the old one could confuse the new executable.
close(fd);
// We can now execute new program. It will inherit current open
// file descriptors and it will see "C.txt" on file descriptor 1
char *args[]={"cat","A.txt","B.txt",NULL};
execvp(args[0],args);
// If we reach this point, `execvp` failed.
perror("execvp failed");
exit(EXIT_FAILURE);
I'm having trouble using read(), write(), and open() to copy a file into a new directory. I'm able to create a new file with open() and I'm able to write to the terminal with write() but I was under the assumption that if I passed my int file descriptor from open to write, write would write to the specified file.
In addition to this, once I open a file the permissions are all funky and I can only open it as root. Each time I create a file, it's completely empty but I can see the entire file I just read be printed out in the terminal.
I've read through the man files for each system call but I'm still not grasping what the issue is here.
Here's my code:
void *scanFile( s_request *request )
{
//TODO Print out all struct variables.
pthread_mutex_lock(&mutex);
int readFileReference;
int writeFileReference;
int bufferSize = request->bufferSize;
int numberOfBytesRead = bufferSize;
int *buffer[bufferSize];
if ((readFileReference = open(request->file, O_RDONLY)) == -1)
{
printf("Failed to open file. Aborting.\n");
exit(EXIT_FAILURE);
}
if ((writeFileReference = open("newfile.txt", O_CREAT | O_APPEND | O_RDWR) == -1))
{
printf("Failed to open write file. Aborting.\n");
exit(EXIT_FAILURE);
}
while ((read(readFileReference, buffer, numberOfBytesRead)) != 0)
{
write(writeFileReference, buffer, bufferSize);
bzero(buffer, bufferSize);
}
pthread_mutex_unlock(&mutex);
}
Fixed the issue:
Updated code:
void *scanFile( s_request *request )
{
//TODO Print out all struct variables.
pthread_mutex_lock(&mutex);
int readFileReference;
int writeFileReference;
int bufferSize = request->bufferSize;
int numberOfBytesRead = bufferSize;
int *buffer[bufferSize];
if ((readFileReference = open(request->file, O_RDONLY)) == -1)
{
printf("Failed to open file. Aborting.\n");
exit(EXIT_FAILURE);
}
if ((writeFileReference = open("newfile.txt", O_CREAT | O_WRONLY, 0777)) == -1)
{
printf("Failed to open write file. Aborting.\n");
exit(EXIT_FAILURE);
}
while ((read(readFileReference, buffer, numberOfBytesRead)) != 0)
{
write(writeFileReference, buffer, bufferSize);
bzero(buffer, bufferSize);
}
close(writeFileReference);
close(readFileReference);
pthread_mutex_unlock(&mutex);
}
if ((writeFileReference = open("newfile.txt", O_CREAT | O_APPEND | O_RDWR) == -1))
You've got the parentheses wrong on this line. (It's correct on the readFileReference line.) What you want is:
if ((writeFileReference = open("newfile.txt", O_CREAT | O_APPEND | O_RDWR)) == -1)
^^^ ^^^
Your original version of that line was calling open(), performing a comparison to -1, then assigning the result of that comparison to writeFileReference.
Additionally:
As noted by Zan Lynx in a comment, you need to pass a permissions value to open() when creating a file. 0666 is typically correct -- it'll create the file as readable/writable. (It's modified by the process umask, so it'll end up creating the file as 0644 under a typical configuration.)
You need to save the return value from read somewhere and pass that as the third argument to write (instead of bufferSize). Otherwise, your program will write more bytes than were read, e.g. when copying small files.
Technically, you should be checking the return value from write(). Just like read(), it's not guaranteed to complete a full write every time it's called. (In practice, when working with regular files, it will always either complete the write or return an error, but there are some esoteric situations where this may not be the case.)
You don't need to bzero() the buffer after writing from it.
I have been looking int the man 3 tcgetattr (as I want to change the terminal settings in a program) and found this.
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions,
const struct termios *termios_p);
Question:
I would like to know what fd should mean? (it seems to be stdin, yet I do not understand why)?
Background
My comprehension is that terminal is input and output together, as my understanding was that a /dev/tty or /dev/pty yields stdin, stdout and stderr together.
fd stands for file descriptor, which is a reference to an OS file object. Because it is a reference, multiple different file descriptors may refer to the same file object.
stdin, stdout, and stderr are FILE * objects -- actual pointers to stdio FILE datastructures. You can get the file descriptor that refers to the underlying OS object with the fileno function.
So there's two levels of indirection going on here. The FILE * could all refer to the same FILE, but they don't; there are 3 separate FILE objects for stdin, stdout, and stderr. These FILE objects each contain a file descriptor, normally 0, 1 and 2 (I say normally -- the OS/lib sets them up this way and they'll only change if you explicitly change them in your program). The 3 file descriptors will then normally all refer to the same underlying OS object, which is a single terminal object.
Since there's (generally) only one terminal, and all these file descriptors (usually) refer to it, it doesn't matter which fd (0, 1, or 2) you use as the first argument to tcsetaddr.
Note that it is possible for these fds to refer to different objects -- if you start your program with redirections (< or > in the shell) then one or more of them will refer to some other file object and not the terminal.
To simplify Thomas Dickey's and Chris Dodd's answers, the typical code to select which descriptor is used to refer to the terminal is
int ttyfd;
/* Check standard error, output, and input, in that order. */
if (isatty(fileno(stderr)))
ttyfd = fileno(stderr);
else
if (isatty(fileno(stdout)))
ttyfd = fileno(stdout);
else
if (isatty(fileno(stdin)))
ttyfd = fileno(stdin);
else
ttyfd = -1; /* No terminal; redirecting to/from files. */
If your application insists on having access to the controlling terminal (the terminal the user used to execute this process), if there is one, you can use the following new_terminal_descriptor() function. For simplicity, I'll embed it in an example program:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
int new_terminal_descriptor(void)
{
/* Technically, the size of this buffer should be
* MAX( L_ctermid + 1, sysconf(_SC_TTY_NAME_MAX) )
* but 256 is a safe size in practice. */
char buffer[256], *path;
int fd;
if (isatty(fileno(stderr)))
if (!ttyname_r(fileno(stderr), buffer, sizeof buffer)) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
if (isatty(fileno(stdout)))
if (!ttyname_r(fileno(stdout), buffer, sizeof buffer)) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
if (isatty(fileno(stdin)))
if (!ttyname_r(fileno(stdin), buffer, sizeof buffer)) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
buffer[0] = '\0';
path = ctermid(buffer);
if (path && *path) {
do {
fd = open(path, O_RDWR | O_NOCTTY);
} while (fd == -1 && errno == EINTR);
if (fd != -1)
return fd;
}
/* No terminal. */
errno = ENOTTY;
return -1;
}
static void wrstr(const int fd, const char *const msg)
{
const char *p = msg;
const char *const q = msg + ((msg) ? strlen(msg) : 0);
while (p < q) {
ssize_t n = write(fd, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
return;
else
if (errno != EINTR)
return;
}
}
int main(void)
{
int ttyfd;
ttyfd = new_terminal_descriptor();
if (ttyfd == -1)
return EXIT_FAILURE;
/* Let's close the standard streams,
* just to show we're not using them
* for anything anymore. */
fclose(stdin);
fclose(stdout);
fclose(stderr);
/* Print a hello message directly to the terminal. */
wrstr(ttyfd, "\033[1;32mHello!\033[0m\n");
return EXIT_SUCCESS;
}
The wrstr() function is just a helper function, that immediately writes the specified string to the specified file descriptor, without buffering. The string contains ANSI color codes, so that if successful, it will print a light green Hello! to the terminal, even if the standard streams were closed.
If you save the above as example.c, you can compile it using e.g.
gcc -Wall -Wextra -O2 example.c -o example
and run using
./example
Because the new_terminal_descriptor() uses ctermid() function to obtain the name (path) to the controlling terminal as a last resort -- this is not common, but I wanted to show here it is easy to do if you decide it is necessary --, it will print the hello message to the terminal even when all streams are redirected:
./example </dev/null >/dev/null 2>/dev/null
Finally, in case you are wondering, none of this is "special". I am not talking about the console terminal, which is the text-based console interface many Linux distributions provide as an alternative to the graphical environment, and the only local interface most Linux servers provide. All of the above uses just normal POSIX pseudoterminal interfaces, and will work just fine using e.g. xterm or any other normal terminal emulator (or Linux console), in all POSIXy systems -- Linux, Mac OS X, and the BSD variants.
Agreeing with #chris-dodd that the file descriptors corresponding to the streams stdin, stdout and stderr usually refer to the same terminal, some points are needed for the original question:
the fd parameter (file descriptor) for tcgetattr and tcsetattr has to be for a terminal.
you could obtain this for a stream using fileno, e.g., fileno(stdin).
POSIX defines constants for the default assignment of file descriptors to stdin, stdout and stderr as STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO. However, it is possible to reopen any of the streams (or use dup or dup2) and alter the actual file descriptor.
while you can obtain a file descriptor for a stream, if you are doing anything interesting with the terminal attributes, that can interfere with the buffering used for streams. If you have to mix the two (file descriptor and stream), do the changes to terminal attributes before reading or writing the stream.
you can also get a file descriptor using an open on the terminal device. This is useful if the streams are redirected, and your application has to work with the terminal. Password prompts do this.
the terminal device can be read from the program tty (even if stdin, etc., are redirected).
programs can check a file descriptor using isatty to see if it is a terminal. If a stream is redirected to a file or pipe, it is not a terminal.
Further reading:
11. General Terminal Interface (POSIX)
What is the difference between stdin and STDIN_FILENO?
By way of experiment I have found myself the following answer:
Each of the triple stderr,stdout,stdin was able to be used to change the terminal settings via the tcsetattr(fd....) function. Once a change was effectuated reading tcgsetattr(stdin....),tcgsetattr(stdout....), and also tcgsetattr(sterr....) returned the same content in the struct termios.h which could be verified via memcmp(&termios_stdin,&termios_stdout,sizeof(struct termios)) == 0
also maybe the man page stated somewhat indirectly this
tcgetattr() gets the parameters associated with the object referred by
fd and stores them in the termios structure referenced by termios_p.
This function may be invoked from a background process; however, the
terminal attributes may be subsequently changed by a foreground
process.
about fd hence the object referred by fd is always the same terminal
I've created a code that should be able to copy a file a user suggests. What I am wondering is this: how do I set the output file mode and how do I determine what the output file mode permissions will be in this code?
#include <stdio.h>
#include <stdlib.h>
int main()
{
char c;
char source_file, target_file;
FILE *in, *out;
printf("Enter name of file to copy\n");
gets(source_file);
printf("Enter name of file to copy to\n");
gets(target_file);
in = (source, O_RDONLY);
out = (target_file, O_CREAT|WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
/* error handing */
if( in == NULL )
{
printf("Error. \n");
exit(0);
}
printf("Enter the copied file name \n");
gets(target_file);
out = fopen(target_file, "w");
/*error handing*/
if( out == NULL )
{
fclose(in);
printf("File opening error.\n");
exit(0);
}
while(( c = fgetc(in) ) != EOF )
fputc(c,out);
fclose(in);
fclose(out);
return 0;
}
Controlling file permissions using standard I/O
One of the demerits of the standard I/O library is that you can't control the permissions on the files that are created, primarily because such permissions are rather platform-specific (more so than the C standard allows for, anyway). The POSIX open() function allows you to control the permissions on the file as it is created.
With a POSIX-like system, you can use the chmod() or fchmod() system calls. You need to know that your rw-rw-rw- pattern is octal 0666.
chmod(target_file, 0666);
fchmod(fileno(out), 0666);
The functions can fail; you should check that they don't.
You can also use the umask() function or (with care) the umask command to influence the default permissions. For example, setting umask 022 in the shell means that files will not be created that are writable by group or others.
Revising the modified code
You don't need to worry about the permissions on a file you open for reading (or, at least, you seldom need to do so).
Worrying about the permissions on the file you write to is more normal.
Your current code proposal is:
in = (source, O_RDONLY);
out = (target_file, O_CREAT|WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
This does not invoke open(), and assigns an integer value to the two FILE * variables, which should be generating compiler warnings. Note that the comma expressions evaluate the LHS and then the RHS of the expression, yielding the RHS as the overall value. O_RDONLY is classically 0; the combination of S_IRUSR etc terms is not zero.
If you're going to open the file with those options, then you need something like:
int fd_i = open(source_file, O_RDONLY);
if (fd_i < 0)
…report error opening source_file…
FILE *in = fdopen(fd_i, "r");
if (in == 0)
…report error creating file stream for source_file…
int fd_o = open(target_file, O_CREAT|WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if (fd_o < 0)
…report error opening target_file…
FILE *out = fdopen(fd_o, "w");
if (out == 0)
…report error creating file stream for target_file…
However, I would probably not use fdopen() for the input file — I'd use fopen() directly as you did originally — but I might use it for the output file.
In a POSIX environment when using system calls to manipulate text files (open(), close(), read(), write()), is there a way to to check to see if I actually closed a file descriptor when using close(file_descriptor)?
Example:
int main(int argc, char **argv)
{
int input_file; // file descriptor for input file
int output_file; // file descriptor for output file
input_file = open(argv[1], O_RDONLY));
ouput_file = open(argv[2], ...file properties & permissions and crap.....);
// blah blah blah...
close(input_file);
close(output_file);
// what can I code here to check if the file descriptor was actually closed?
}
The easiest way is probably to just check the return value of your first close.
if (close(input_file)) {
perror("close");
} else {
// all good
}
This is the only thread-safe way. In a multi-threaded program, another thread could get a new file descriptor that recycles the fd number of the one that was just closed.
In single-threaded code only, where nothing can recycle an fd between close and check:
If you wish to check whether a file descriptor is valid afterwards then you can use any function that consumes a file descriptor and check it's error code. The most non-intervening and light weight is probably fcntl/F_GETFL
if (fcntl(fd, F_GETFL) < 0 && errno == EBADF) {
// file descriptor is invalid or closed
}
You could also just call close a second time and it can also fail with EBADF.
if (close(fd) && errno == EBADF) {
// file descriptor is invalid or closed
} else {
// we successfully closed fd *now* (e.g. first close failed with EINTR)
// or there was a different error
}
close doesn't modify errno on success, but we only read errno when close returns non-zero, meaning it has been set.
You can check the return value of close() and the error number accordingly.
from http://pubs.opengroup.org/onlinepubs/009695399/functions/close.html
Upon successful completion, 0 shall be returned; otherwise, -1 shall
be returned and errno set to indicate the error.
[...]
The close() function shall fail if:
[EBADF]
The fildes argument is not a valid file descriptor.
The only save method to check whether a file descriptor is invalid is to explicitly set it to (for example) -1 on definition and every time it successfully has been closed.
The could be done using macro:
#define FD_INVALID (-1)
#define FD_CLOSE_AND_INVALIDATE(fd) \
do { \
if (-1 == close(fd)) \
{ \
perror("close failed"); \
} \
else \
{ \
(fd) = FD_INVALID; \
} \
} while (0)
int main(int argc, char ** argv)
{
int input_file = FD_INVALID; /* Properly initialise file descriptor for input file */
int output_file = FD_INVALID; /* Properly initialise file descriptor for output file */
input_file = open(argv[1], O_RDONLY));
ouput_file = open(argv[2], ...);
/* Do something. */
FD_CLOSE_AND_INVALIDATE(input_file);
FD_CLOSE_AND_INVALIDATE(output_file);
/* To check if the file descriptor was actually closed just test for FD_INVALID. */
}