How to write a file, then make it executable in C? - c

I am trying to write a program that writes a file with some text in it, then makes that file executable. This is what I have:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char name[] = "foo";
FILE * fp;
fp = fopen(name, "w");
fprintf(fp, "This file should be executable.\n");
execl("/usr/bin/chmod", "/usr/bin/chmod", "+x", name, NULL);
return 0;
}
The problem I am having that running execl seems to remove the contents of the file I wrote. If I remove the execl, it works as expected, and writes a file with the desired text. But when I leave in the execl, it writes a file, makes it executable, but the file is blank. How do I make it so the file still has the text in it, and is executable?

The problem is that output to the file is buffered. When you call execl(), you replace the process with chmod, but never write the stdio buffer to the file.
You need to call fclose(fp) before execl() to force everything to be written. You could also use fflush(fp), but fclose() more complete.

Creating the file with the wrong permissions and then changing them is not what you want to do. Just create the file with the desired permissions in the first place. If you want the file to be executable you can (with one caveat mentioned below) run:
/* CAUTION: all error checking omitted for clarity */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int
main(void)
{
char name[] = "foo";
FILE * fp;
int fd = open(name, O_RDWR | O_CREAT, 0777);
fp = fdopen(fd, "w");
fprintf(fp, "This file should be executable.\n");
return 0;
}
This will create the file with mode 0777 (modified by the umask) when executed. (eg, if umask is 111, the executable bits will not be set)
But, if you do want to create the file with the wrong permissions and then change them, don't exec out to /usr/bin/chmod. Just use chmod(2). eg chmod(name, 0777);
+x is convenient if you are just adding a permission, but since you are creating the file you are in complete control and you know what the permissions are.

Related

How to write something in a existing .txt file with c program?

I am trying to write "File opened" in a existing test.txt file with C program. But I must not create a test.txt file with the program. I have to write with in a existing file.
I tried but can't.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
int main() {
char sentence[1000] = "File opened";
fopen("test.txt", "w");
fprintf("%s", sentence);
fclose();
return 0;
}
The error showing me is:
error: too few arguments to function 'fclose'
How can I do that? Please help me.
In future recommend that you do some searching for either existing questions that people have asked or some research. ie look up the manual or spec for fopen: https://man7.org/linux/man-pages/man3/fopen.3.html
Especially since your code doesn't compiled as it has errors, with the fopen, fprintf and fclose all missing arguments or assignment variables. Reading those errors and the compiler messages would have guided you to the solution. This is what it would look like with those fixed and the "w+" option used:
#include <stdio.h>
#include <stdlib.h>
int main() {
char sentence[1000] = "File opened";
FILE *file = fopen("test.txt", "w+");
fprintf( file, "%s", sentence);
fclose( file );
return 0;
}
This is a good tutorial if you want to know more: https://www.geeksforgeeks.org/basics-file-handling-c/

Why is direct I/O file not being written to?

I am trying to understand direct I/O. To that end I have written this little toy code, which is merely supposed to open a file and write a text string to it:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char **argv) {
char thefile[64];
int fd;
char message[64]="jsfreowivanlsaskajght";
sprintf(thefile, "diotestfile.dat");
if ((fd = open(thefile,O_DIRECT | O_RDWR | O_CREAT, S_IRWXU)) == -1) {
printf("error opening file\n");
exit(1);
}
write(fd, message, 64);
close(fd);
}
My compile command for Cray and GNU is
cc -D'_GNU_SOURCE' diotest.c
and for Intel it is
cc -D'_GNU_SOURCE' -xAVX diotest.c
Under all three compilers, the file diotestfile.dat is created with correct permissions, but no data is ever written to it. When the executable finishes, the output file is blank. The O_DIRECT is the culprit (or, more precisely I guess, my mishandling of O_DIRECT). If I take it out, the code works just fine. I have seen this same problem in a much more complex code that I am trying to work with. What is it that I need to do differently?
Going on Ian Abbot's comment, I discovered that the problem can be solved by adding an alignment attribute to the "message" array:
#define BLOCK_SIZE 4096
int bytes_to_write, block_size=BLOCK_SIZE;
bytes_to_write = ((MSG_SIZE + block_size - 1)/block_size)*block_size;
char message[bytes_to_write] __attribute__ ((aligned(BLOCK_SIZE)));
(System I/O block size is 4096.)
So that solved it. Still can't claim to understand everything that is happening. Feel free to enlighten me if you want. Thanks to everyone for the comments.
Well, you need to rethink the question, because your program runs perfectly on my system, and I cannot guess from it's listing where the error can be.
Have you tested it before posting?
if the program doesn't write to the file, probably a good idea is to see about the return code of write(2). Have you done this? I cannot check because on my system (intel 64bit/FreeBSD) the program runs as you expected.
Your program runs, giving no output and a file named diotestfile.dat appeared in the . directory with contents jsfreowivanlsaskajght.
lcu#europa:~$ ll diotestfile.dat
-rwx------ 1 lcu lcu 64 1 feb. 18:14 diotestfile.dat*
lcu#europa:~$ cat diotestfile.dat
jsfreowivanlsaskajghtlcu#europa:~$ _

C fopen() unable to open file in /tmp

I am following the basic C programming tutorial on tutorialspoint.com
I have the following program which generates a file in /tmp called test.txt:
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("/tmp/test.text", "w+");
fprintf(fp, "This is testing for fprintf...\n");
fputs("This is testing for fputs...\n", fp);
fclose(fp);
return 0;
}
Then I have a second program which just tries to open that file for reading:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
FILE *fp = NULL;
fp = fopen("/tmp/test.txt", "r");
if (fp == NULL) {
printf("NULL!!!\n");
}
printf("%s\n", strerror(errno));
fclose(fp);
return 0;
}
However, when I try to run the program that opens the file, I get the following output:
NULL!!!
No such file or directory
Segmentation fault
If I modify the code to point to the same file in my home directory, it works correctly. It seems that, for some reason, I am not able to open files in the /tmp directory (via fopen)... And just to be clear, I am able to change to /tmp and cat the contents of the test.txt file just fine. Permissions look normal on it as well, 664 with my user as the owner and group.
The only other specifics that I can think of that might have to do with my system is that I am on Elementary OS Juno, I am using g++ 7.3.0 (clang also gives the same result), and I have separate encrypted partitions for my OS root and home...
Any thoughts on what might be causing this?
This was simply caused by an incorrect file extension, .txt vs.text as pointed out by #yano

fopen() and command line arguments

I'm trying to write a program that will read the first character in a text file. If I use ./a.out myfile.txt it works as intended but if I use ./a.out <myfile.txt I get Segmentation fault: 11. The reason why I'm trying to include the <is because this what is in the spec of the assignment. The below code is just a simplified example that i've made that has the same issue:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int func(int argc, char **argv){
FILE *fp;
int test = 0;
fp = fopen(argv[1], "r");
fscanf(fp, "%i", &test);
printf( "current file: %s \n", argv[1]);
}
int main(int argc, char **argv){
func(argc, argv);
}
Is there any way I can get it to accept the argument as <myfile.txt?
No, nor should you try. Files redirected this way will appear at stdin and you should use that instead (hint: check argc).
If you want to use a file if specified, but otherwise stdin, use something like:
if (argc > 1)
fp = fopen(argv[1], "r");
else
fp = stdin;
In your command ./a.out <myfile you redirect stdin to myfile. This means reading from stdin is actually reading from myfile. So, in this case your argc == 1, so argv[1] you use to open is NULL (see main spec on its arguments). fopen crashes when uses NULL name.
You may do your utility in another way: always read stdin. When you need file to input do like this: cat myfile | ./a.out. This is very nice approach and worth considering.

How to transfer parameters from execl to scanf?

I want to transfer some parameters from one program to another.
For example, here are two programs. a.c compiled as a
#include <stdio.h>
int main() {
char a[10];
scanf("%s", a);
printf("%s\n", a);
return 0;
}
and e.c compiled as e:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
char *input = "here it is";
execl("./a", "a", "a", NULL);
return 0;
}
Actually, I would like to transfer the parameter "a" from the e.c to a.c so that once I execute the program "e", it will print out
a
a
However, I find out that excel cannot pass the parameters to the specified program.
If I cannot modify the program a.c, how can I execute this program using another program using standard input?
If you can't modify a.c, then you'll need to write the data to some file, then reset stdin to read from that file:
FILE *fp = fopen("somefile", "w");
if (fp == NULL)
// handle error
fputs(input, fp);
fclose(fp);
if (freopen("somefile", "r", stdin) == NULL)
// handle error
unlink("somefile");
execl("./a", "a", NULL);
The unlink system call makes sure the file disappears when a is done with it.
Note: you might be able to use a pipe instead of a file, because the message is so small that it will fit inside a pipe's buffer. That's not a reliable solution for larger messages, though -- if you want to use a pipe, you need to fork.

Resources