My c code is
size_t n=0;
char *str = (char *)malloc(sizeof(char)* 1000)
FILE *fp = popen(" cat /conf/a.txt" ,"r" );
// my program comes in this function only if /conf/a.txt exists
getline(&str, &n, fp); <== crash if fp is null
My debugger shows that sometimes i get fp as null and hence my program crashes at line 6 . Sometimes i get valid pointere and it passes .
What is it , that controls this behaviour . I can't find problem in above code . Some help is appreciated .
I know I can have a check of fp==null but that is not my question . I just want to know , knowing that file is definitely present why is fp coming as null in some scenarios .
man of popen says The popen() function returns NULL if the fork(2) or pipe(2) calls fail, or if it cannot allocate memory.
i checked after crash and system is having enough memory ..
strerror and errno are your friends.
Example from the C++ references linked:
/* strerror example : error list */
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
FILE * pFile;
pFile = fopen ("unexist.ent","r");
if (pFile == NULL)
printf ("Error opening file unexist.ent: %s\n",strerror(errno));
return 0;
}
Example output:
Error opening file unexist.ent: No such file or directory
Using this method of checking errno after a failure will allow you to better diagnose your issue as it will print a more specific error message. There are many reasons a file can't be opened: no permission, bad path, file is locked from another process, IO errors during reading, etc. Ultimately your question seems to be asking why the open failed. Using these tools will answer that for you.
Update For Tag Change:
I've referenced and linked to C++ resources, but sterror and errno are both available in C as well by including errno.h.
popen() also fails if too many file handles are open in one process. I had one case in a server app, that was scanning one directory periodically for files.There was one scenario were no fclose call was made. So after some hours we reached the limit of 1024 open file handles on from that moment consecutive popen() calls would fail.
You can use ps -aux | grep {PROC_NAME} to retrieve the process id.
Then use sudo ls -l /proc/{PROC_ID}/fd to see the list of open file descriptors.
Related
what is a proper way of writing to /proc or /sys filesystem in linux in c ?
Can I write as I would in any other file, or are there special considerations I have to be aware of?
For example, I want to emulate echo -n mem > /sys/power/state. Would the following code be the right way of doing it?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
FILE *f;
f = fopen("/sys/power/state", "w");
if(f == NULL) {
printf("Error opening file: /sys/power/state\n");
exit(1);
}
fprintf(f,"%s","mem");
fclose(f);
}
Your approach lacks some error handling in the write operation.
The fprintf (or fwrite, or whatever you prefer to use) may fail, e.g. if the kernel driver behind the sysfs file doesn't like what you're writing. E.g.:
echo 17 > /sys/class/gpio/export
-bash: echo: write error: Invalid argument
In order to catch those errors, you MUST check the output of the fprintf to see if all characters that you expected to write were written, and you should also check the output of ferror(). E.g. if you're writing "mem", fprintf should return 3 and there should not be any error set in the stream.
But one additional thing is missing: sysfs are not standard files. For the previous write error to be returned correctly you MUST disable buffering in your stream, or otherwise the fprintf (or fwrite)) may happily end without any error. You can do that with setvbuf like this just after the fopen.
setvbuf (f, NULL, _IONBF, 0);
I have the following code to find the release of the Linux distribution that I am using.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
return print_osinfo();
}
int print_osinfo()
{
FILE *fp;
extern FILE* popen();
char buffer[128];
int index = 0;
memset(buffer,0,sizeof(buffer));
fp = popen("/etc/centos-release", "r");
if(!fp)
{
pclose(fp);
fp = popen("/etc/redhat-release", "r");
if(!fp)
{
pclose(fp);
return 1;
}
}
while(fgets(buffer, sizeof(buffer), fp)!= NULL)
{
printf("%s\n",buffer);
}
pclose(fp);
return 0;
}
If I run the above code on Ubuntu 14.04 I get the following error.
sh: 1: /etc/centos-release: not found
I fail to understand why it is not trying to open redhat-release and then return -1. Also, is there a way to prevent the above error from being displayed on the screen?
popen is a function more suited for accessing the output of a subprocess than for simply accessing the contents of a file. For that, you should use fopen. fopen takes a file path and a mode as arguments, so all you would need to do is replace your popens with fopens and it should work perfectly.
If you really want to use popen, it takes a shell command as it's first argument, not a filename. Try popen("cat /etc/centos-release","r"); instead.
Now, you might be a bit confused, because both of these functions return a FILE pointer. fopen returns a pointer to the file you passed as an argument. popen, however, returns a pipe pointing to the output of the command you passed to it, which C sees as a FILE pointer. This is because, in C, all i/o is file access; C's only connection to the outside world is through files. So, in order to pass the output of some shell command, popen creates what C sees as a FILE in memory, containing the output of said shell command. Since it is rather absurd to run a whole other program (the shell command) just to do what fopen does perfectly well, it makes far more sense to just use fopen to read from files that already exist on disk.
I want to write a stream into one FILE *fp at the same time the stream should be copied onto another fp too is there a better way to write my debug function by eliminating one fprintf?
const int logflag=1;
#define debug(args ...) if (logflag) { FILE *flog = fopen("test.log", "a+"); fprintf( flog, args); fclose(flog); } fprintf(stderr, args);
int main()
{
debug("test"); // writes test into both stderr and flog
debug("test2");
}
The short answer is no, it's two different file pointers and you can only write to one at a time. Actually, dup still doesn't help you because it closes the duplicated file descriptor:
"dup2() makes newfd be the copy of oldfd, closing newfd first if necessary"
from the dup2 man-pages
However, if your goal is to have both a log to the screen and to a file, you are better served by using the tools Linux already provides you. A generally good practice (I don't remember the source for this) is to have a program print its output and debugging to a stdout/stderr and let the calling user determine how to handle the output.
Following this, if all of your output goes to stderr, you can do the following when executing the program:
$ ./program 2>&1 | tee file.log
I am basically trying to check if a particular file exists or not. For that I am using the test command of Unix.
sprintf(execbuf, "%s if test -r %s ; then true; else exit; fi;",
execbuf, st->file, NO_FILE);
It works fine, but I do not want to exit if the file is not here, rather it should return FAIL.
I am not able to figure out how to make the program return FAIL. I was thinking of using the exit code from the above command, but still I am not able to figure out how to use that exit code outside the Linux command in the program.
I'd recommend you rather just use the access() call, and not execute external shell commands to figure this out.
Just be aware that such cases are subject to race conditions - the file might exist when you call access() (or execute a shell command that determines whether the file exists), but it might be gone when you actually need it later on. If that's a problem for you, just open() the file, and use the file descriptor later on when you actually need it for I/O.
If you're not married to what your doing right now, then I'd suggest using stat:
#include <sys/stat.h>
#include <stdio.h>
int main (int argc, char** argv[])
{
struct stat sts;
if (stat(argv[1], &sts) == -1 && errno == ENOENT)
printf ("The file %s doesn't exist...\n", argv [1]);
else
printf("The file exists\n");
This will tell you if it exists or not. If you dont' want to pass it command line, parameter 1 is a const char*, so just pass it the file name.
While debugging some code I got something like below:
#include<stdio.h>
int main()
{
FILE *fb = fopen("/home/jeegar/","r");
if(NULL == fb)
printf("it is null");
else
printf("working");
}
Here in fopen I gave a somewhat valid path name but not a filename. Shouldn't fopen return NULL then? But it does not return null!
Edit:
If I give path of valid directory in fopen then it will print working:
If I give path of invalid directory in fopen then it will print it is null
Edit:
spec says
Upon successful completion, fopen() shall return a pointer to the object
controlling the stream. Otherwise, a null pointer shall be returned.
so here whether error code set or not, it MUST return NULL
And error code setting is an extansion to ISO C standard standard.
ERROR IS ALSO NOT GOING TO SET HERE
#include<stdio.h>
#include <errno.h>
int main()
{
errno = 0;
FILE *fb = fopen("/home/jeegar/","r");
if(fb==NULL)
printf("its null");
else
printf("working");
printf("Error %d \n", errno);
}
OUTPUT IS
workingError 0
I think that in Unix everything (directories included) is considered to be file so fopen should work on them.
The posix man page man 3p fopen says, in the section ERRORS:
The fopen() function shall fail if:
[...]
EISDIR The named file is a directory and mode requires write access.
(Emphasis mine). Since you are not requesting write access, and chances are that the path you use is a directory, the function does not fail.
About what can you use with a FILE* that refers to a directory, I have no idea.
As you might be very well aware that pretty much everything on Linux system is a file, if not a file then its a process (corrections & remarks welcome :) ) Directory is treated like a file which lists other files (Reference from TLDP); so opening to read a directory as a file is a valid operation and thus you do not get any error. Although trying to write to it is not allowed, so if you open directory in write or append mode, the fopen operation will fail (this has been very well mentioned is other responses & link to fopen documentation). Most of the file operation like read & write operations on this file stream will fail with the error stating that its a directory. Only use which could be found was finding the size of the file (directory in this case) using fseek to SEEK_END & ftell (which will most likely give a result of 4096).
Regarding using errno to get meaningful messages, you can use perror which is in stdio.h & pass message which will be added before the error message or strerror which is in string.h & pass errno which is in errno.h
Hope this helps!
How to check that errno?
You can check errno for example:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
errno = 0;
fp = fopen("file.txt", "r");
if ( errno != 0 )
{
// Here you can check your error types:
perror("Error %d \n", errno);
exit(1);
}
}
Error types you can find at http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html Error section.