Error in the systemcalls C11 for do file i/o - c

Hi I'm on Linux WSL Debian and I've the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
int fd = open("file.dat", O_RDONLY | O_WRONLY);
write(fd, "ciao", strlen("ciao"));
close(fd);
}
Why don't works?

Many bugs:
Must include unistd.h for the declaration of write.
O_RDONLY and O_WRONLY (and O_RDWR) are mutually exclusive. If you want to open a file for read and write you have to use O_RDWR. Since you are not actually reading from the file, you should use O_WRONLY alone.
It is very rare to want to use O_WRONLY without O_CREAT and one (exactly one; these three are also mutually exclusive) of O_APPEND, O_EXCL, and O_TRUNC. Assuming that you do want O_CREAT, you must also supply a third argument, which should be the octal constant 0666 unless you have a specific reason to use some other value.
write(fd, "ciao", strlen("ciao")) should be write(fd, "ciao\n", strlen("ciao\n")) unless you have a specific reason to be creating a text file with an incomplete last line.
Because you are writing to a file, you need to check for errors on all three of the open, write, and close calls. (The fact that close can fail is a bug in its specification, but one that we are permanently stuck with. It's safe to ignore errors in close for files that were opened for reading, but not writing.)
Also some style corrections:
The code as shown only needs unistd.h, fcntl.h, and string.h; it should also be including stdio.h, because it should also be making calls to perror. None of the other headers should be included (unless this is cut down from a much larger program).
Declare main as int main(void) unless you are actually going to use argc and argv.
Don't use the C99 license to fall off the end of main; the last line of main should be return 0;.
For historical reasons, in C, the opening curly brace of a function definition should always be placed on a line by itself, even if all other opening curly braces are "cuddled" with their parent control flow construct.

You need the header <unistd.h> to get the declarations of write() and close(). The only other header you need is <fcntl.h> for open().
I've also kept <stdio.h> so I can use perror() if open() fails.
Since you're only writing to the file, you don't need O_RDONLY in the open modes. If you want to read and write, you should use O_RDWR instead. These three flags are mutually exclusive.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
int fd=open("file.dat",O_WRONLY);
if (fd < 0) {
perror("open failed");
return 1;
}
write(fd,"ciao",strlen("ciao"));
close(fd);
}
Note that this will not create the file if it doesn't already exist. If you want that, you need to add the O_CREAT flag and another argument with the permissions that should be given to the file if it's created.

Related

Printing to text file but file remains empty

#include <stdio.h> //for printf
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
//#define STDOUT_FILENO 1
// define STDERR_FILENO 2
int main(){
// mode_t mode = S_IROTH | S_IRUSR | S_IWUSR;
mode_t mode = S_IRUSR | S_IWUSR;
close(1);
int fildes = open("hello_world.txt", O_CREAT | O_TRUNC | O_RDWR, mode);
printf("Hi! My Name is \n" );
close(fildes);
return 0;
}
From what I learned, "Hi! My Name is" should be printed to "hello_world.txt". It works well in Linux virtual machine which my professor provided.
But in my machine (I'm using remote WSL in vscode), "hello_world.txt" is empty. Can I fix this problem?
printf does not necessarily write anything. Typically, it buffers data and defers writes until the buffers are full. stdout is automatically flushed when the process exits, but you've closed the file descriptor before that happens, so the write fails. Try fflush(stdout) before you close the underlying file descriptor. (This assumes that the hacky open actually gives you the underlying file descriptor of stdout. That should happen and in most cases will, but it certainly is not guaranteed. You should use freopen if you want to do this reliably.)

How does open() have two definitions, as shown in the man-page?

The man page of open() shows open has two definitions.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
I was trying to make a wrap around open to add a backtrace for some debugging. The definition in fnctl.h shows it as a variable argument function
int open(const char *path, int oflag, ... );
But how can I know whether mode is present or not?
The mode argument is not needed for all open flags. For example, if opening an existing file for reading, there's no need to set the file mode flags. On the other hand, when creating a file, you need to include the mode.
See the documentation for open (or your local manual page) to see if you need to include the argument or not.
open() function -
int open (const char *filename, int flags[, mode_t mode])
mode is only used when file is created. No need when file is already present but it won't affect also in either way .
If flags include O_CREAT, there must be a third argument (mode). If not, or if the file already exists, the third argument (if supplied) will be ignored.
Since open is actually variadic, you can call it with any number of arguments (>2), but they will have no effect on the operation.
You will need to extract the mode argument using stdarg.h. The C variadic function feature does not provide any mechanism for a function to find out how many arguments it was called with, so all you can do is check the flag.

What's wrong with my code? (Printing File names inside an Archive File)

I'm trying to write a program to open an archive file from Unix, read the files in and print what files are inside the archive and just the filename. Below is my code -- it compiles, but I got some weird output in the terminal -- e.g. ?;?U?. It should just display 2 txt file names. Can someone take a look at my code and give me some guidance on what I'm missing? Thank you!
EDIT: I made some changes to my code, but it's still not working. Any suggestion or help is greatly appreciated.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <string.h>
#include <ar.h>
int main (int argc, char **argv)
{
int input_fd;
//char buffer[25];
struct ar_hdr my_ar;
if (argc != 2) {
printf("Error", argv[0]);
exit(EXIT_FAILURE);
}
//open the archive file (e.g., hw.a)
input_fd = open(argv[1], O_RDONLY);
if (input_fd == -1)
{
perror("Can't open input file\n");
exit(-1);
}
while (read(input_fd, my_ar.ar_name, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
close(input_fd);
return 0;
}
I don't see anywhere that you are defining what a my_hdr is, yet you are printing one of its supposed members here:
printf("%s\n", my_ar.ar_name);
You also never set ar_name to anything either.
I see you read some data into a variable named buffer, but you never actually copy that buffer into ar_name which I am assuming is your intent.
while (read(input_fd, buffer, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
The POSIX documentation explicitly states that the archive file format is not described, on the grounds that several incompatible such formats already exist, so you'll definitely need to study the documentation provided by your OS if you're going to use <ar.h> instead of the ar command-line utility.
Here are some details that might help, based on the Solaris implementation:
An archive file starts with a magic cookie string which you are neither verifying nor skipping,
Each header object is followed by the content of the actual object in the archive, which you are not stepping over,
The first object in the archive is an unnamed object containing the archive's symbol table,
If an object's name is more than fifteen bytes long, it will be stored in the archive's string table, which appears to be part of the symbol table.
Note that these points, or details thereof, may vary on your OS. As I said at the beginning, study the documentation provided by your OS.

libevent: raise event on file change

I have the following code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <event.h>
void fd_cb(int fd,short event,void *arg){
printf("changed\n");
}
int main(int argc, const char* argv[]){
struct event eoh_ev;
FILE *fp;
int fd;
fp=fopen("/var/log/syslog","rw");
fd=fileno(fp);
event_init();
event_set(&eoh_ev,fd,EV_READ|EV_WRITE,fd_cb,NULL);
event_add(&eoh_ev,NULL);
event_dispatch();
return 0;
}
As you can see, I'm trying to call fd_cb(...) when something is written to /var/log/syslog.
The problem is, "changed" never gets printed!
I'm running the code as root.
Many thanks in advance,
Libevent is designed to work on the same file descriptors that poll or select support. Those system calls are not designed to check for file change events. They are designed to return when a file descriptor can be read or written without blocking, something that isn't very meaningful for regular files (reads and writes to regular files either never block or can always block, depending on how you look at it). In other words - libevent on file descriptors other than sockets, pipes and fifo:s will not work.
There are other mechanisms for checking if a file has changed, but those are not portable.

Using MinGW how can I create a file without the read-only file attribute?

When I attempt to overwrite an existing file, I get a "permission denied" error.
I noticed that the file which is created has the "Read-only" attribute set. When I manually unset this I can then overwrite the file. Is there some flag I can pass to open() which will automatically unset this when I create a file?
Below is a bare bones example which illustrates the problem. The first run works, but the second produces the "permission denied" error.
Thanks,
Zach (New to MingW/Windows 7)
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, char ** argv) {
int fid;
double data = 12.0;
if ( (fid = open("junk.data", O_WRONLY | O_CREAT | O_BINARY)) == -1 ) {
printf("ERROR opening.\n\terror is:%s\n", strerror(errno));
return 1;
}
write(fid, &data, sizeof(double));
close(fid);
return 0;
}
I tried both 0644 and S_IRUSR | S_IWUSR (with sys/stat.h included) and either works.
Make sure that you actually add it as third argument of open, instead as new term into the surrounding parentheses (as happened for me first, and compiles just fine)
open has a three-parameter variant:
int open(const char *pathname, int flags, mode_t mode);
That third parameter allows you to specify the mode bits on Unix-type systems, but should be enough to set minimal r/w permissions on windows. (Check out the man page for details.)

Resources