C: Executing and outputing shell commands in C - c

Aside from using popen() (as was discussed in this question) is this a valid way of doing it ?
Say we had a program who's name is hexdump_dup and wanted the program to output the exact output of the hexdump command.
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755); // (line 8)
write(fd, "/usr/bin/hexdump $#;", 20); // (line 9)
close(fd);
return (0);
}
Also could someone briefly explain what line 8 and 9 do, and how afterwards the command gets executed ? Like when, where does it say to execute the command or what makes the command execute ?

After this
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755); // (line 8)
write(fd, "/usr/bin/hexdump $#;", 20);
you need to execute hexdump_dup executable, for that you need to use either system() or exec() family function. For e.g
system("./hexdump_dup 1 2 3"); /* after creating binary file(hexdump_dup) & writing command into it, you need to run it, for that use system() or exec() */
This
fd = open("hexdump_dup", O_CREAT | O_TRUNC | O_WRONLY, 0755);
will create the hexdump_dup binary if it doesn't exist before & if exists before it will truncate its content to 0. You can refer the man page of open() , it says
int open(const char *pathname, int flags, mode_t mode);
The argument flags must include one of the following access
modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening
the file read-only, write-only, or read/write, respectively.
O_CREAT
If the file does not exist it will be created. The
owner (user ID) of the file is set to the effective
user ID of the process.
O_TRUNC
If the file already exists and is a regular file and
the open mode allows writing (i.e., is O_RDWR or
O_WRONLY) it will be truncated to length 0. If the
file is a FIFO or terminal device file, the O_TRUNC
flag is ignored.
Lastly this
write(fd, "/usr/bin/hexdump $#;", 20);
writes 20 bytes containing array of characters /usr/bin/hexdump $#; in this case into a file where fd points i.e it will put this into hexdump_dup file.
Here $# means when you execute hexdump_dup like
./hexdump_dup 1 2 3
it will take all the parameters to be passed.

Related

why if I redirect the STD_OUT on a file and write to STD_OUT the file remains empty?

I can't solve a problem with standard output laugh, I'm on Unix operating system, so the file-descriptor of STD_IN = 0, STD_OUT = 1, STD_ERR = 2, basically I tried to close the descriptor associated with the standard output, and then overwrite it with that of the file I want to write to, but when I open the file it is empty.
CODE:
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
/*special file's descriptor --use it or constants in unistd.h-- */
#define STD_IN 0 /*standard input stream*/
#define STD_OUT 1 /*standard output stream*/
#define STD_ERR 2 /*standard output-error stream*/
int main(unsigned int num_of_args, char** args)
{
if(num_of_args != 3)
{
write(STD_ERR, "Few argouments.\nThe use = ./executable <message> <file>\n", 69);
return -1;
}
int file_des= open(args[2], O_CREAT, 0640);
if(file_des < 0)
{
write(STD_ERR, "Error, we couldn't open file.\n", 31);
return -1;
}
//I close the descriptor associated with STD_OUT
close(STD_OUT);
//I copy the descriptor associated with fil_des on the first available descriptor(so STD_OUT)
dup(file_des);
write(STD_OUT, args[1], sizeof(args[1]));
close(file_des);
return 0;
}
It should be fine, I don't see any errors, but when I open the file I don't find the message written.
Because? help me
This code is incorrect:
int file_des= open(args[2], O_CREAT, 0640);
Per the POSIX documentation for open() (note the bolded portion - my bolding):
SYNOPSIS
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int oflag, ...);
int openat(int fd, const char *path, int oflag, ...);
DESCRIPTION ...
Values for oflag are constructed by a bitwise-inclusive OR of flags
from the following list, defined in <fcntl.h>. Applications shall
specify exactly one of the first five values (file access modes) below
in the value of oflag:
O_EXEC
Open for execute only (non-directory files). The result is unspecified if this flag is applied to a directory.
O_RDONLY
Open for reading only.
O_RDWR
Open for reading and writing. The result is undefined if this flag is applied to a FIFO.
O_SEARCH
Open directory for search only. The result is unspecified if this flag is applied to a non-directory file.
O_WRONLY
Open for writing only.
Your open() call needs to include either O_WRONLY or O_RDWR as you are writing to the file:
int file_des= open(args[2], O_CREAT | O_WRONLY, 0640);
The Problem
The problem here is that the file descriptor which is being assigned to file_des isn't really 1.
int test = dup(file_des);
fprintf(stderr, "%d", test);
3
Now, what could the reason be?
The problem is with how you are opening your file. O_CREAT creates a file if it does not exist.
There is no specification whether you are opening the file for read, write, or read/write.
Since you are associating your file to the file descriptor of stdout, you must use the O_WRONLY flag as well.
int file_des = open(args[2], O_CREAT | O_WRONLY, 0640);

How to check if a file exist, delete content or create it

i would like to check if a file exist, delete content if it exists or create it if not.
I have tried :
open("screenshot.bmp", O_CREAT | O_RDWR | O_TRUNC);
But the file don't update if it already exists, if it doesn't the file is created correctly.
if ((fd = open("screenshot.bmp", O_CREAT, S_IRWXU)) > -1)
return (-1);
close (fd);
if ((fd = open("screenshot.bmp", O_TRUNC)) > -1)
return (-1);
But the file looks corrupted/empty after that (it should be filled by the rest of my code)
I also tried other ways.
Thanks for help !
Try using FILE *fd = fopen("screenshot.bmp", "w");
Accorsing to tutorialspoint:
FILE *fopen(const char *filename, const char *mode)
"w"
Creates an empty file for writing. If a file with the same name already exists, its content is erased and the file is considered as a new empty file.
Update:
OP says fopen(...) isn't allowed, but...
According to the docs you can achieve the same result as the fopen(...) call using:
open (filename, O_WRONLY | O_CREAT | O_TRUNC, mode)
For example (from the docs):
The following example opens the file /tmp/file, either by creating it (if it does not already exist), or by truncating its length to 0 (if it does exist). In the former case, if the call creates a new file, the access permission bits in the file mode of the file are set to permit reading and writing by the owner, and to permit reading only by group members and others.
If the call to open() is successful, the file is opened for writing.
#include <fcntl.h>
...
int fd;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
char *filename = "/tmp/file";
...
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, mode);
...

writing files at a low level

I was reading the GNU C PROGRAMMING TUTORIAL online, and get some confusion on the code example for low level read & write.
The code is as below:
#include <stdio.h>
#include <fcntl.h>
int main()
{
char my_write_str[] = "1234567890";
char my_read_str[100];
char my_filename[] = "snazzyjazz.txt";
int my_file_descriptor, close_err;
/* Open the file. Clobber it if it exists. */
my_file_descriptor = open (my_filename, O_RDWR | O_CREAT | O_TRUNC);
/* Write 10 bytes of data and make sure it's written */
write (my_file_descriptor, (void *) my_write_str, 10);
fsync (my_file_descriptor);
/* Seek the beginning of the file */
lseek (my_file_descriptor, 0, SEEK_SET);
/* Read 10 bytes of data */
read (my_file_descriptor, (void *) my_read_str, 10);
/* Terminate the data we've read with a null character */
my_read_str[10] = '\0';
printf ("String read = %s.\n", my_read_str);
close (my_file_descriptor);
return 0;
}
I compiled the code with gcc without issue. And run the first time, it is also ok. Output as below:
$ ./lowLevelWrite
String read = 1234567890.
The problem comes when i run the program second time:
$ ./lowLevelWrite
String read = .
Seems the code fails to write the string "1234567890" to the file second time. As we know from the GNU C manual, O_RDWR | O_CREAT | O_TRUNC these flag should allow us to truncate the file to 0 every time and then write to the file. I am not sure why it fails from second time execution.
Can anybody help me out of this confusion?
When you're creating a file with open() you need to pass a third argument, the permission modes:
my_file_descriptor = open (my_filename, O_RDWR | O_CREAT | O_TRUNC, 0664);
0664 is the permissions rw-rw-r--: readable and writable by the owner and group, readable by everyone else. These permissions will be further masked by your umask.
Since you didn't pass this argument, open() used random stack garbage, and this probably didn't include write permission. So you couldn't open the file for writing when it already exists.

Copy program: File Permission Denied

My following program, which copies a file, won't allow me to copy files because of "permission denied". However, I gave it permissions.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int fdinput, fdoutput; //file pointers
char arrbuf[5000]; //size of what can be read in file
ssize_t bytesR, bytesW;//number of what input returns
mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH |S_IXOTH ;
fdinput = open(argv[1], O_RDONLY); //pointing to read file
fdoutput = open(argv[2], O_WRONLY);//pointing to write file
if(fdinput == -1){
perror("the source file cant be opened");
return 1;
}
if(fdoutput == -1){
perror("the written file cant be opened");
return 2;
}
while((bytesR = read(fdinput, arrbuf, sizeof arrbuf)) > 0){
bytesW = write(fdoutput, arrbuf, (ssize_t) bytesR);
}
close(fdinput);
close(fdoutput);
return 0;
}
The problem is in the call to the system call open() for the destination file (i.e.: the file to be created as a result of the copy):
fdoutput = open(argv[2], O_WRONLY);
Making possible the creation of the destination file
First, the call above to open() opens the file with the given name by argv[2], only if it already exits. Otherwise, the system call fails (errno is set to ENOENT) and perror() produces:
the written file cant be opened: No such file or directory
In order to create the file if it does not exist yet, the O_CREAT flag has to ORed together with O_WRONLY.
Truncating an already existing destination file
If the destination file already exist you surely want to truncate the length of that already existing file to zero at the moment of open()ing. That can by achieved by ORing the O_TRUNC flag together with the other flags.
Providing the permissions for the file to be created
Let's look at the open() system call's prototype:
int open(const char *path, int oflag, ...);
The ... at the end is to specify a kind of optional argument. That argument is used by open() only when a new file is being created. It provides the mode bits to be applied for the file to be created. This is not exactly the permissions for the file to be created, but it is strongly related to them (for more info see: file mode creation mask).
You created mode of type mode_t but just forgot to pass it to open().
With all exposed above in mind, your call should look like:
fdoutput = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, mode);

C system calls open / read / write / close and O_CREAT|O_EXCL

Given the following code (it's supposed to write "helloworld" in a "helloworld" file, and then read the text):
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FNAME "helloworld"
int main(){
int filedes, nbytes;
char buf[128];
/* Creates a file */
if((filedes=open(FNAME, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
S_IRUSR | S_IWUSR)) == -1){
write(2, "Error1\n", 7);
}
/* Writes hello world to file */
if(write(filedes, FNAME, 10) != 10)
write(2, "Error2\n", 7);
/* Close file */
close(filedes);
if((filedes = open(FNAME, O_RDONLY))==-1)
write(2, "Error3\n", 7);
/* Prints file contents on screen */
if((nbytes=read(filedes, buf, 128)) == -1)
write(2, "Error4\n", 7);
if(write(1, buf, nbytes) != nbytes)
write(2, "Error5\n", 7);
/* Close file after read */
close(filedes);
return (0);
}
The first time I run the program, the output is:
helloworld
After that every time I to run the program, the output is:
Error1
Error2
helloworld
I don't understand why the text isn't appended, as I've specified the O_APPEND file.
Is it because I've included O_CREAT?
It the file is already created, shouldn't O_CREAT be ignored?
O_EXCL forces the file to be created. If the file already exists, the call fails.
It is used to ensure that the file has to be created, with the given permissions passed in the third parameter. In short, you have these options:
O_CREAT: Create the file with the given permissions if the file doesn't already exist. If the file exists, it is opened and permissions are ignored.
O_CREAT | O_EXCL: Create the file with the given permissions if the file doesn't already exist. If the file exists, it fails. This is useful in order to create lockfiles and guarantee exclusive access to the file (as long as all programs which use that file follow the same protocol).
O_CREAT | O_TRUNC: Create the file with the given permissions if the file doesn't already exist. Otherwise, truncate the file to zero bytes. This has more of the effect we expect when we think "create a new blank file". Still, it keeps the permissions already present in the existing file.
More information from the manual page:
O_EXCL
When used with O_CREAT, if the file
already exists it is an error and
the open() will fail. In this context,
a symbolic link exists, regardless of
where it points to. O_EXCL is broken
on NFS file systems; programs which
rely on it for performing locking
tasks will contain a race condition.
The solution for performing atomic
file locking using a lockfile is to
create a unique file on the same file
system (e.g., incorporating hostname
and pid), use link(2) to make a link
to the lockfile. If link() returns 0,
the lock is successful. Otherwise, use
stat(2) on the unique file to check if
its link count has increased to 2, in
which case the lock is also
successful.

Resources