Can't set file with "echo" from a program via system() - c

I'm try to use system("echo 1 > /sys/class/afile") to set a file to 1.
If I use it on console it works well, but if I run my C program it shows me:
sh: echo: I/O error
I already try to set it with following code:
char i[1];
i[0]=1;
int fd1 = open("/sys/class/afile",O_CREAT | O_WRONLY);
int ret = write(fd1,i,strlen(i));
if(ret > 0)
printf("Ok\n");
else
printf("nOk\n");
close(fd1);
The result is "Ok" but the file didn't change.

strlen(i) yields undefined behavior because i is not a string; the array lacks room for null termination. Also, i[0]=1; does not put a '1' character in the array, but rather puts a byte with value 1, which is a "^A character".
Instead try write(fd, "1", 1) - no need for any variables or strlen.

Here
int fd1 = open("/sys/class/afile",O_CREAT | O_WRONLY);
you need to provide mode in the third argument of open() since you are using O_CREAT.
From the manual page of open
int open(const char *pathname, int flags, mode_t mode);
It says
O_CREAT
If pathname does not exist, create it as a regular file.
The mode argument specifies the file mode bits be applied when
a new file is created. This argument must be supplied when
**O_CREAT or O_TMPFILE is specified in flags;**
For e.g you can try this
int fd1 = open("/sys/class/afile",O_CREAT | O_WRONLY, 0664);
And do check return value of open() system call with perror().
Also here
char i[1];
int ret = write(fd1,i,strlen(i));
as #R pointed, strlen(i) causes undefined behavior since i is declared of 1 byte & there is no space for \0 char to null terminate the char buffer.

There are some problems in your program, first I wasn't able to write file without sudo persmission:
~/Documents/src/progs : $ echo 1 > /sys/class/afile
bash: /sys/class/afile: Permission denied
If you try to do the same thing with some file in local directory(not in root folders), you should be able to write your file like this:
#include <stdio.h>
int main() {
FILE* pFile = fopen("afile", "w");
fprintf(pFile, "1\n");
fclose(pFile);
}

Related

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.

read() returns Bad file descriptor on a valid file descriptor

In the following program,
int main()
{
int fd;
char buf[8]={};
remove("file.txt");
fd = creat("file.txt",0666);
write(fd,"asdf",5);
perror("write");
lseek(fd,0,SEEK_SET);
perror("lseek");
read(fd,buf,5);
perror("read");
printf("%s\n",buf);
return 0;
}
My expected output is
write : Success
lseek : Success
Read : Success
asdf
But it shows
wriet : Success
lseek : Success
Read : Bad file descriptor
Can anybody tell me the reason? I can see that the string "asdf" if successfully written to file.txt
From the manpage:
The creat() function shall behave as if it is implemented as follows:
int creat(const char *path, mode_t mode)
{
return open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
}
So, the file is opened in write-only mode and thus you cannot read.
If you need to read and write, use open(...) directly with O_RDWR instead of O_WRONLY.
The 0666 you specified just indicates the permissions in the file system the file gets created with.
If you just want to do normal file I/O you could also use the high-level APIs such as fopen.

Segmentation fault while system() in C

I want to use the "base64" script of linux to encode the data and get it in C.
When I try to compile
char a[200];
strcpy(a, "Hello");
printf("%s", a);
I get the output
Hello
Now whenever I try the code
char a[200];
strcpy(a, system("echo Hello | base64"));
printf("%s", a);
I get the output
aGVsbG8K
Segmentation fault
Even when I remove the "printf" statement, I get the same
aGVsbG8K
Segmentation fault
I want to save the value of the output of
system("echo Hello | base64")
in 'a' and not display it. Please help
If you read the documentation for system you'll discover that it doesn't return a string - it's defined as:
int system(const char *command);
The return value is the return status of the command or -1 if there's an error. You can't get the output using system - the output of the command(s) you run will go straight to stdout.
To get the output from another command you could use something like popen.
FILE *myfile;
char buffer[1024];
myfile=popen("echo Hello | base64","r");
if(myfile)
{
while(fgets(buffer,1024,myfile))
{
printf("%s",buffer);
}
pclose(myfile);
}
Here
strcpy(a, system("echo Hello | base64"));
system() doesn't stores it's result into array a as system() job is to execute the command provided in the argument & print it on console i.e stdout buffer. From the manual page of system
system() executes a command specified in command by calling
/bin/sh -c
command, and returns after the command has been completed.
There is one way to solve the problem i.e instead of printing system() output on stdout you can redirect its output to a file & then read that from file & print. For example
int main(void) {
close(1); /* stdout file descriptor is avilable now */
/* create the file if doesn't exist, if exist truncate the content to 0 length */
int fd = open("data.txt",O_CREAT|O_TRUNC|O_RDWR,0664); /* fd gets assigned with lowest
available fd i.e 1 i.e nowonwards stdout output
gets rediredcted to file */
if(fd == -1) {
/* #TODO error handling */
return 0;
}
system("echo Hello | base64"); /* system output gets stored in file */
int max_char = lseek(fd,0,2);/* make fd to point to end, get the max no of char */
char *a = malloc(max_char + 1); /* to avoid buffer overflow or
underflow, allocate memory only equal to the max no of char in file */
if(a == NULL) {
/* #TODO error handling if malloc fails */
return 0;
}
lseek(fd,0,0);/* from beginning of file */
int ret = read(fd,a,max_char);/* now read out put of system() from
file as array and print it */
if(ret == -1) {
/* #TODO error handling */
return 0;
}
a[ret] = '\0';/* \0 terminated array */
dup2(0,fd);/*fd 0 duplicated to file descriptor where fd points i.e */
printf("output : %s \n", a);
/* to avoid memory leak, free the dynamic memory */
free(a);
return 0;
}
My above suggestion is a temporary fix & I won't recommend this, instead use [popen] as suggested by #chris Turner (http://man7.org/linux/man-pages/man3/popen.3.html) which says
The popen() function opens a process by creating a pipe, forking,
and
invoking the shell. Since a pipe is by definition unidirectional, the
type argument may specify only reading or writing, not both; the
resulting stream is correspondingly read-only or write-only.
For example
int main(void) {
char buf[1024];
FILE *fp = popen("echo Hello | base64","r");
printf("%s\n",fgets(buf,sizeof(buf),fp));
return 0;
}

C: Executing and outputing shell commands in 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.

C open() function fails. Are my parameters wrong?

This function fails to open the file. Are my parameters wrong or what could be causing this problem?
int CreateFile(const char *filename){
char filepath[strlen(filename) + 3];
sprintf(filepath, "./%s", filename);
int fd = open(filepath, O_CREAT, O_APPEND, S_IWGRP);
if(fd == -1) printf("file read failed\n");
return fd;
}
Xcode prints only "file read failed" to the console. I tried to run this via Terminal aswell but that didn't help either.
I fixed an issue pointed by NetMage:
int CreateFile(const char *filename){
char filepath[strlen(filename) + 3];
sprintf(filepath, "./%s", filename);
int fd = open(filepath, O_CREAT|O_APPEND, S_IWGRP);
if(fd == -1) printf("file read failed\n");
return fd;
}
Unfortunately that didn't fix the issue
Step 1 - Verify that filepath is being set correctly, either by printing it to the terminal or examining it in a debugger.
Step 2 - Verify that the file exists in that path, and that its permissions are set so that you can open it. If filepath is "./foo", then a file named foo had better exist in the current working directory (the directory from which you ran the program), and it needs to have at least read permission.
Step 3 - If the file does not exist, verify that you have permission to create new files in the current working directory.
Step 4 - If after doing all of that you still get an error, check errno. It will give you some additional information beyond "it didn't work."
#include <errno.h>
...
if(fd == -1)
{
switch( errno )
{
case EACCESS: // permission issues
handle_permission_issue();
break;
case EEXIST: // file already exists and you used O_CREAT and O_EXCL
handle_already_exists_issue();
break;
case EFAULT: // bad path
handle_bad_path_issue()
break;
...
}
printf("file read failed\n");
}
NetMage has pointed out one problem - your flags need to be bitwise-OR'd together, rather than listed as separate arguments. Surprised the compiler didn't yell at you over that.
The open function takes only one parameter for oflags, which must be bit-ored together:
#include <errno.h>
#include <string.h>
int fd = open(filepath, O_CREAT|O_APPEND, S_IWGRP);
if (fd == -1) printf("file read failed: %s\n", strerror(errno));
Per the POSIX documentation for open() (somewhat reformatted, and note the bolded text):
SYNOPSIS
#include <sys/stat.h> #include <fcntl.h>
int open(const char *path, int oflag, ...);
...
Values for oflag are constructed by a bitwise-inclusive OR of flags
from the following list, defined in . 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.
...
You need to include at least one of those five flags, perhaps like:
int fd = open(filepath, O_WRONLY|O_CREAT|O_APPEND, S_IWGRP);
Note that other failures may still occur. As noted in the comments, you're prepending "./" to the file name, which may cause problems if, for example, you get passed "/tmp/filename" and the tmp directory doesn't exist in your current working directory, as open() will not create missing directories in any path.

Resources