I wrote code below
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd = 3;
char c[100] = "Testing\n";
ssize_t nbytes = write(fd, (void *) c, strlen(c));
return 0;
}
compiled/linked, and executed
$ ./io
$ ./io 3> io_3.txt
The first line produced no output. The second line gave me file io_3.txt containing Testing.
This is all expected behaviour (I guess).
Even if in my tests it produced the expected output,
I am not certain if, to avoid potential problems, undefined behavior, etc., I should do anything prior to the first write, like checking if fd=3 is in use (and in that case, how... this may apply), if it is suitably open, etc.
And I am not certain if I should perform some action after the last write, for the same reasons.
Perhaps the way I did is "non-risky", the only potential issue being that nothing is written, which I could detect by checking the value of nbytes... I wouldn't know.
Any clarification is welcome.
If you write a program like this, executing it without fd 3 open is a usage bug. Normally the only file descriptors that should be used by number without having opened them yourself are 0 (stdin), 1 (stdout), and 2 (stderr). If a program needs to take additional pre-opened file descriptors as input, the standard idiom is to pass the fd numbers on the command line or environment variables rather than hard-coding them. For example:
int main(int argc, char **argv) {
if (argc<2 || !isdigit(argv[1][0])) return 1;
int fd = strtol(argv[1], 0, 0);
char c[100] = "Testing\n";
ssize_t nbytes = write(fd, (void *) c, strlen(c));
return 0;
}
In practice, a trivial program like yours is probably safe with the write just failing if fd 3 wasn't open. But as soon as you do anything that might open file descriptors (possibly internal to the implementation, like syslog, or date/time functions opening timezone data, or message translation catalogs, etc.), it might happen that fd 3 now refers to such an open file, and you wrongly attempt a write to it. Using file descriptors like this is a serious bug.
Related
I have a sample program that takes in an input from the terminal and executes it in a cloned child in a subshell.
#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/wait.h>
#include <sched.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
int clone_function(void *arg) {
execl("/bin/sh", "sh", "-c", (char *)arg, (char *)NULL);
}
int main() {
while (1) {
char data[512] = {'\0'};
int n = read(0, data, sizeof(data));
// fgets(data, 512, stdin);
// int n = strlen(data);
if ((strcmp(data, "exit\n") != 0) && n > 1) {
char *line;
char *lines = strdup(data);
while ((line = strsep(&lines, "\n")) != NULL && strcmp(line, "") != 0) {
void *clone_process_stack = malloc(8192);
void *stack_top = clone_process_stack + 8192;
int clone_flags = CLONE_VFORK | CLONE_FS;
clone(clone_function, stack_top, clone_flags | SIGCHLD, (void *)line);
int status;
wait(&status);
free(clone_process_stack);
}
} else {
exit(0);
}
}
return 0;
}
The above code works in an older Linux system (with minimal RAM( but not in a newer one. Not works means that if I type a simple command like "ls" I don't see the output on the console. But with the older system I see it.
Also, if I run the same code on gdb in debugger mode then I see the output printed onto the console in the newer system as well.
In addition, if I use fgets() instead of read() it works as expected in both systems without an issue.
I have been trying to understand the behavior and I couldn't figure it out. I tried doing an strace. The difference I see is that the wait() return has the output of the ls program in the cases it works and nothing for the cases it does not work.
Only thing I can think of is that read(), since its not a library function has undefined behavior across systems. But I can't agree as to how its affecting the output.
Can someone point me out to why I might be observing this behavior?
EDIT
The code is compiled as:
gcc test.c -o test
strace when it's not working as expected is shown below
strace when it's working as expected (only difference is I added a printf("%d\n", n); following the call for read())
Thank you
Shabir
There are multiple problems in your code:
a successful read system call can return any non zero number between 1 and the buffer size depending on the type of handle and available input. It does not stop at newlines like fgets(), so you might get line fragments, multiple lines, or multiple lines and a line fragment.
furthermore, if read fills the whole buffer, as it might when reading from a regular file, there is no trailing null terminator, so passing the buffer to string functions has undefined behavior.
the test if ((strcmp(data, "exit\n") != 0) && n > 1) { is performed in the wrong order: first test if read was successful, and only then test the buffer contents.
you do not set the null terminator after the last byte read by read, relying on buffer initialization, which is wasteful and insufficient if read fills the whole buffer. Instead you should make data one byte longer then the read size argument, and set data[n] = '\0'; if n > 0.
Here are ways to fix the code:
using fgets(), you can remove the line splitting code: just remove initial and trailing white space, ignore empty and comment lines, clone and execute the commands.
using read(), you could just read one byte at a time, collect these into the buffer until you have a complete line, null terminate the buffer and use the same rudimentary parser as above. This approach mimics fgets(), by-passing the buffering performed by the standard streams: it is quite inefficient but avoids reading from handle 0 past the end of the line, thus leaving pending input available for the child process to read.
It looks like 8192 is simply too small a value for stack size on a modern system. execl needs more than that, so you are hitting a stack overflow. Increase the value to 32768 or so and everything should start working again.
I have a basic program that opens a file contained in the same directory as the C file (in root). The file is called myfile1, which is a simple file containing text.
This program is supposed to open the file, count the number of characters and display it. For some reason, I compile the program, run it with a.out and the program gets input from the user and finishes when Ctrl+D is pressed, when it is supposed to get input from the file.
Any ideas as to what could be going on? Thank you very much, here is what I have so far:
#include <fcntl.h>
void main(){
char buff[512];
int fd = 0;
int j=0;
long total=0;
if(fd=open("myfile1",O_RDONLY)<0){
printf("Error");
return 1;
}
while((j=read(fd,buff,512))>0)
total = total+j;
printf("%d\n",total);
close(fd);
return 0;
}
The problem is with your if condition: fd=open("myfile1",O_RDONLY)<0. Less-than comparison have higher precedence than assignment. It should be (fd=open("myfile1",O_RDONLY))<0.
In your code, as ketlat said, the if logic is problematic.
If you put a debug print inside your code and check the fd, you'll see
Code:
if(fd=open("myfile1",O_RDONLY)<0){
printf("Error");
return 1;
}
printf("obtained fd = %d\n", fd);
O/P:
obtained fd = 0
Reason
< has higher preceidence over =. Hence, when the open() is success, it will return a non-negative value, which is not less than 0, and the comparison open("myfile1",O_RDONLY)<0 will evaluate to false , represented as 0, and the same will be assigned to fd.
Nw, this fd value will be used in your read() call. FD 0 means stdin or the standard input. So, as per your logic, the code behaves correctly.
However, to achieve your goal, you need to change
fd=open("myfile1",O_RDONLY)<0
to
(fd=open("myfile1",O_RDONLY))<0
With the changed code, a sample run is likely to yield an o/p like
obtained fd = 3.
Be notified, YMMV.
I'd like to:
redirect stdout/stderr
to an in-memory buffer, rather than to disk
while leaving stdout/err running as normal
I know how to use dup2 and freopen to:
redirect stdout/err to a file of my choosing
while leaving stdout/err running as normal
...but I'm not sure about item 2?
REASON: I want to post-process the data going to stdout/err (that's coming from 3rd party code), and then send it to a webserver - while leaving the application running as normal.
WHY? ... because right now when our app runs we're seeing critical data go to stdout (and err) from 3rd party code, and it's a PITA getting users to try and mess around with local logfiles. I'd rather capture "the last N bytes" into a ring buffer, post-process it, and then - iff there's a problem that the user reports - send that to server.
WHY AVOID FILES? ... because this is for code that has to run on iOS as well as desktop, and "constantly writing to a file" is something I want to avoid, since I don't want it as a file anyway. Also ... I'd have to worry about maintaining that file, proactively trimming its size, etc.
Stdout is a basic file handle, which means its a pointer.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv);
int main (argc,argv)
int argc;
char **argv;
{
stdout = &(*stderr);
fprintf(stdout, "test");
return 0;
}
By declaring stdout to point to a different file handle, this program redirects stdout. To test it, compile it as test, and then type ./test > t and ./test 2> t on the command line.
The redirected stdout pipe will still output the word test, whereas the redirected stderr pipe will not.
Since files are streams, read this section of the GNU C Library for how to create memory streams. This example creates a text buffer to dynamically resize for holding input, and redirects stdout to point to it.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv);
int main (argc,argv)
int argc;
char **argv;
{
char *bp;
size_t size;
FILE *stream;
stream = open_memstream (&bp, &size);
stdout = &(*stream);
fprintf(stdout, "test");
fflush(stdout);
fprintf(stderr, "buf = %s, size = %d\n", bp, size);
fclose (stream);
free(bp);
return 0;
}
Pay careful attention to the sections on flushing the buffers when switching between input and output in the manual.
I have a program where I need to set the permissions of a file (say /home/hello.t) using chmod and I have to read the permissions to be set from a file. For this I first read the permissions into a character array and then try to modify the permissions of the file. But I see that permissions are set in a weird manner.
A sample program I have written:
main()
{
char mode[4]="0777";
char buf[100]="/home/hello.t";
int i;
i = atoi(mode);
if (chmod (buf,i) < 0)
printf("error in chmod");
}
I see that the permissions of the file are not set to 777. Can you please help me out on how to set the permissions of the file after reading the same from a character array.
The atoi() function only translates decimal, not octal.
For octal conversion, use strtol() (or, as Chris Jester-Young points out, strtoul() - though the valid sizes of file permission modes for Unix all fit within 16 bits, and so will never produce a negative long anyway) with either 0 or 8 as the base. Actually, in this context, specifying 8 is best. It allows people to write 777 and get the correct octal value. With a base of 0 specified, the string 777 is decimal (again).
Additionally:
Do not use 'implicit int' return type for main(); be explicit as required by C99 and use int main(void) or int main(int argc, char **argv).
Do not play with chopping trailing nulls off your string.
char mode[4] = "0777";
This prevents C from storing a terminal null - bad! Use:
char mode[] = "0777";
This allocates the 5 bytes needed to store the string with a null terminator.
Report errors on stderr, not stdout.
Report errors with a newline at the end.
It is good practice to include the program name and file name in the error message, and also (as CJY pointed out) to include the system error number and the corresponding string in the output. That requires the <string.h> header (for strerror()) and <errno.h> for errno. Additionally, the exit status of the program should indicate failure when the chmod() operation fails.
Putting all the changes together yields:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
char mode[] = "0777";
char buf[100] = "/home/hello.t";
int i;
i = strtol(mode, 0, 8);
if (chmod (buf,i) < 0)
{
fprintf(stderr, "%s: error in chmod(%s, %s) - %d (%s)\n",
argv[0], buf, mode, errno, strerror(errno));
exit(1);
}
return(0);
}
Be careful with errno; it can change when functions are called. It is safe enough here, but in many scenarios, it is a good idea to capture errno into a local variable and use the local variable in printing operations, etc.
Note too that the code does no error checking on the result of strtol(). In this context, it is safe enough; if the user supplied the value, it would be a bad idea to trust them to get it right.
One last comment: generally, you should not use 777 permission on files (or directories). For files, it means that you don't mind who gets to modify your executable program, or how. This is usually not the case; you do care (or should care) who modifies your programs. Generally, don't make data files executable at all; when files are executable, do not give public write access and look askance at group write access. For directories, public write permission means you do not mind who removes any of the files in the directory (or adds files). Again, occasionally, this may be the correct permission setting to use, but it is very seldom correct. (For directories, it is usually a good idea to use the 'sticky bit' too: 1777 permission is what is typically used on /tmp, for example - but not on MacOS X.)
I've written code that should ideally take in data from one document, encrypt it and save it in another document.
But when I try executing the code it does not put the encrypted data in the new file. It just leaves it blank. Someone please spot what's missing in the code. I tried but I couldn't figure it out.
I think there is something wrong with the read/write function, or maybe I'm implementing the do-while loop incorrectly.
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main (int argc, char* argv[])
{
int fdin,fdout,n,i,fd;
char* buf;
struct stat fs;
if(argc<3)
printf("USAGE: %s source-file target-file.\n",argv[0]);
fdin=open(argv[1], O_RDONLY);
if(fdin==-1)
printf("ERROR: Cannot open %s.\n",argv[1]);
fdout=open(argv[2], O_WRONLY | O_CREAT | O_EXCL, 0644);
if(fdout==-1)
printf("ERROR: %s already exists.\n",argv[2]);
fstat(fd, &fs);
n= fs.st_size;
buf=malloc(n);
do
{
n=read(fd, buf, 10);
for(i=0;i<n;i++)
buf[i] ^= '#';
write(fd, buf, n);
} while(n==10);
close(fdin);
close(fdout);
}
You are using fd instead of fdin in fstat, read and write system calls. fd is an uninitialized variable.
// Here...
fstat(fd, &fs);
// And here...
n=read(fd, buf, 10);
for(i=0;i<n;i++)
buf[i] ^= '#';
write(fd, buf, n);
You're reading and writing to fd instead of fdin and fdout. Make sure you enable all warnings your compiler will emit (e.g. use gcc -Wall -Wextra -pedantic). It will warn you about the use of an uninitialized variable if you let it.
Also, if you checked the return codes of fstat(), read(), or write(), you'd likely have gotten errors from using an invalid file descriptor. They are most likely erroring out with EINVAL (invalid argument) errors.
fstat(fd, &fs);
n= fs.st_size;
buf=malloc(n);
And since we're here: allocating enough memory to hold the entire file is unnecessary. You're only reading 10 bytes at a time in your loop, so you really only need a 10-byte buffer. You could skip the fstat() entirely.
// Just allocate 10 bytes.
buf = malloc(10);
// Or heck, skip the malloc() too! Change "char *buf" to:
char buf[10];
All said it true, one more tip.
You should use a larger buffer that fits the system hard disk blocks, usually 8192.
This will increase your program speed significantly as you will have less access to the disk by a factor of 800. As you know, accessing to disk is very expensive in terms of time.
Another option is use stdio functions fread, fwrite, etc, which already takes care of buffering, still you'll have the function call overhead.
Roni