SIGXFSZ is sent by kernel unless something is printed to stdout? - c

I am learning "Advanced Programming in Unix Environment", and have a problem with exercise no.11 in chapter 10.
In my program, I set RLIMIT_FSIZE to 1024.
So the kernel should send SIGXFSZ to my program when write trying to exceed that limit.
But I found that SIGXFSZ is not send unless something is printed to stdout.
Here is my code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#define BUFFSIZE 100
void xfsz_handler(int signo)
{
fprintf(stderr, "%d, %s\n", signo, strsignal(signo));
}
int main(int argc, char* argv[])
{
int n;
char buf[BUFFSIZE];
struct rlimit fsizeLimit;
fsizeLimit.rlim_cur=1024;
fsizeLimit.rlim_max=1024;
if(setrlimit(RLIMIT_FSIZE, &fsizeLimit) < 0)
{
perror("setrlimit error");
exit(-1);
}
if(signal(SIGXFSZ, xfsz_handler)==SIG_ERR)
{
fprintf(stderr, "set signal handler error for %d\n", SIGXFSZ);
exit(-1);
}
printf("what ever\n"); /* we need this to get SIGXFSZ sent */
while ( (n=read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
{
int byteWrite = 0;
if ( (byteWrite = write(STDOUT_FILENO, buf, n)) < 0)
{
perror("write error");
exit(-1);
}
if(byteWrite!=n)
{
fprintf(stderr, "byteWrite=%d, n=%d\n", byteWrite, n);
exit(-1);
}
}
if (n<0)
{
perror("read error");
exit(-1);
}
return 0;
}
if I comment out the following line in the code, kernel will not transmit SIGXFSZ.
printf("What ever . . . \n");
Why this happens? Thanks in advance.
[root#luaDevelopment ex11]# ./myCopy < /root/workspace/AdvanceProgrammingInTheUnixEnvironment.20140627.tar.bz2 >aa.tar.bz2
byteWrite=24, n=100
[root#luaDevelopment ex11]# make
gcc -o myCopy myCopy.c -std=gnu99 -I../../lib/ -L../../lib/ -lch10
[root#luaDevelopment ex11]# ./myCopy < /root/workspace/AdvanceProgrammingInTheUnixEnvironment.20140627.tar.bz2 >aa.tar.bz2
byteWrite=24, n=100
25, File size limit exceeded
[root#luaDevelopment ex11]#

user3693690 found the answer in Appendix C of the book:
10.11 Under Linux 3.2.0, Mac OS X 10.6.8, and Solaris 10, the signal handler for SIGXFSZ is never called because the loop exits the program on a short write, but write returns a count of 24 as soon as the file’s size reaches 1,024 bytes. When the file’s size has reached 1,000 bytes under FreeBSD 8.0, the signal handler is called on the next attempt to write 100 bytes, and the write call returns −1 with errno set to EFBIG("File too big"). On all four platforms, if we attempt an additional write at the current file offset (the end of the file), we will receive SIGXFSZ and write will fail, returning −1 with errno set to EFBIG.

Related

Is possible to read virtual memory on Unix/Linux? And on Windows?

I am working on Debian GNU/Hurd with Mach. I have been asked to write a program that, given a PID and an address, executes vm_read over the address and prints the result.
This is the code I have written:
#include <mach_error.h>
#include <mach/mig_errors.h>
#include <mach/thread_status.h>
#include <mach/processor_info.h>
#include <mach/i386/vm_param.h>
#include <stdio.h>
#include <stdlib.h>
#include <hurd.h>
#include <string.h>
int main(int argc, char * argv[]) {
if(argc != 3) {
printf ("Wrong arguments: ./vm_read PID address\n");
exit(1);
}
int res;
mach_port_t target_task = pid2task(atoi(argv[1]));
vm_address_t addr = atoi(argv[2]);
vm_offset_t *data;
mach_msg_type_number_t data_count;
res = vm_read (target_task, addr, sizeof(int), &data, &data_count);
if (res != KERN_SUCCESS) {
printf ("Error reading virtual mem (0x%x), %s \n", res,
mach_error_string(res));
exit(1);
}
printf("done\n");
for (int i=0; i<data_count; ++i){
printf("byte %d : %x\n",i,((char*)data)[i]);
}
}
It works correctly, but now I'm asked if it is possible to write a version for Unix/Linux and another for Windows that do the same thing.
I've been searching and it looks like it shouldn't be any problem because both use virtual memory in their procceses, but I'm not sure if there could be complications with permissions or anything else.
For Windows, if you need to read memory from a process, you'll need to request the PROCESS_VM_READ when you get your handle to the process (ReadProcessMemory is the appropriate call). In order to get that Handle, it's usually easier to start the process yourself with OpenProcess.
There's no standard way to access the memory of another process on UNIX, but on Linux, you can do it by reading the special file /proc/pid/mem:
char memfile[32];
snprintf(memfile, sizeof(memfile), "/proc/%s/mem", argv[1]);
int mfd = open(memfile, O_RDONLY);
if (mfd < 0) {
perror("Can't open pid/mem file");
exit(1); }
if (lseek(mfd, (off_t)strtoull(argv[2], 0, 0), SEEK_SET) {
perror("Can't seek to address");
exit(1); }
if (read(mfd, &data, sizeof(data)) <= 0) {
fprintf(stderr, "No data at address %s\n", argv[2]);
exit(1); }

Why doesn't rx receive data from loopback tx on UART?

I have a system running Linux, connected the rx to the tx (loopback) from UART1 and executed this code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(void) {
int iFd = open("/dev/ttyTHS0", O_RDWR);
if (iFd < 0) {
fprintf(stdout, "Error when opening file.\n");
return -1;
}
while (1) {
if (write(iFd, "A", strlen("A")) == -1) {
fprintf(stdout, "Failed to write\n");
return -1;
}
char buff[10] = {
0
};
if (read(iFd, buff, sizeof(buff)) < 0) {
fprintf(stdout, "Failed to read.\n");
return -1;
}
fprintf(stdout, "read: %s\n", buff);
}
close(iFd);
return 0;
}
The thing is it doesn't seem to receive any data. The code simply blocks at read. I can see the data being transmitted on the tx line when looking at it with an oscilloscope, but the rx doesn't receive anything. I have also tried splitting this code in 2 processes, one process for the transmission and another one for the reception, but that doesn't work either.
It is logical. You probably do not send anything as it is sits in the send buffer. Other problem is that you try to receive 10 bytes, but send only one. On many systems serial timeouts are set to infinitive time and you will wait forever for the next 9 bytes.
what to do (point 2 has some variants):
flush the UART file
2a. read only as many bytes as you sent
ioctl(ifd, FIONREAD, &bytes_ready_to_read); and then read only the number available.
2c. change the timeout.

Zombie process - Finding it

I used inotify() function in my program to monitor /proc directory. Now my job is to find when a process become zombie. Then i need output a warning that zombie process was found. At the moment program is recording some of the changes but i don't know how to find zombie process..
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <limits.h>
int main()
{
int inotify_fd, wd;
int num;
char buffer[sizeof(struct inotify_event)+NAME_MAX+1];
struct inotify_event *dogodek;
inotify_fd = inotify_init();
if (inotify_fd==-1) {
perror("inotify_init");
return 1;
}
wd = inotify_add_watch(inotify_fd, "/proc/",IN_ALL_EVENTS);
if (wd==-1) {
perror("inotify_add_watch");
return 1;
}
printf("Map gots descriptor %d\n",wd);
int f;
for (f=0; f<20; f++) {
num=read(inotify_fd, &buffer, sizeof(buffer));
if (num>0) {
dogodek=(struct inotify_event*)buffer;
printf("Access to map with descriptor %d\n", dogodek->wd);
if (dogodek->len>0)
printf("\tevent found: %s\n", dogodek->name);
else
printf("\tevent found in directory!\n");
printf("\tMask of the event %x\n", dogodek->mask);
if (dogodek->mask & IN_ACCESS)
printf("\t\tReading file!\n");
if (dogodek->mask & IN_CREATE)
printf("\t\tCreating file!\n");
if (dogodek->mask & IN_DELETE)
printf("\t\tDeleting file!\n");
if (dogodek->mask & IN_OPEN)
printf("\t\tOpening file!\n");
}
}
inotify_rm_watch(inotify_fd, wd);
return 0;
}
I need example in code not just explation if there is a possibility.
The following applies to Linux (at least):
The status of a process can be found in /proc/[pid]/status.
For a zombie the file /proc/[pid]/cmdline is empty, that is reading from it returns 0 bytes.
For details please see here: http://man7.org/linux/man-pages/man5/proc.5.html

MinGW: reading binary data fail

I'm trying to read a binary file on Windows 7 with a program compiled by MinGW. The binary file is ~10M in size but my program can only read less than 1000 bytes and it thinks it reached EOF.
Here is the code. I'm sure I'm doing something stupid but I just can't seem to find it.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define TS_FILE "foo.data"
int main(void)
{
int fd;
int r;
unsigned char buf[1024];
fd = open(TS_FILE, O_RDONLY|O_BINARY);
printf("fd: %d\n", fd);
if ( fd == -1 )
{
exit(0);
}
for (;;)
{
r = read(fd, buf, 1000);
if ( r != 1000 )
{
printf("read error. %d\n", r);
perror("read");
}
if ( r == 0 )
{
break;
}
}
close(fd);
}
The program will say it reads 736 bytes and that's the EOF.
Could somebody tell me what is going on? Thanks!
Thanks,
In fact, your program is indeed reading the entire file. It reads the file 1000 bytes at a time until there are, in this case, 736 bytes left. Then it reads those final 736 bytes and read returns 736. You are mistakenly treating the inability to read the full 1000 bytes as an error, but it is not an error. If read fails then the error condition is marked by the return value being -1.
Your loop should perhaps be more like this:
for (;;)
{
r = read(fd, buf, 1000);
if (r == -1)
{
printf("read error\n");
perror("read");
exit(1);
}
if (r != 1000)
{
//entire file has been read
break;
}
}
close(fd);
A couple of other points:
The correct type for r is size_t.
Rather than hardcoding 1024 and 1000, you would be better with something like #define BUFFLEN 1024 so that you don't keep repeating those magic values.

Reading from a pty

I'd like to receive (and later process) write(1) and wall(1) messages using a (Unix 98-style) pseudo tty on Linux.
I already have the following minimal implementation:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <utempter.h>
#define BUF_LENGTH 1024
int
main (void)
{
FILE *lf;
int masterfd, slavefd;
char *slave_name = NULL;
char buf[BUF_LENGTH];
size_t nbytes = sizeof(buf);
ssize_t bytes_read;
int exit_code = EXIT_SUCESS;
if ((masterfd = posix_openpt (O_RDWR | O_NOCTTY)) == -1
|| grantpt (masterfd) == -1
|| unlockpt (masterfd) == -1
|| (slave_name = ptsname (masterfd)) == NULL)
exit (EXIT_FAILURE);
if (!(lf = fopen("term.log","w")))
exit (EXIT_FAILURE);
addToUtmp (slave_name, NULL, masterfd);
for (;;)
{
bytes_read = read(masterfd, buf, nbytes);
if (bytes_read <= 0)
break
fwrite (buf, 1, bytes_read, lf);
}
if (bytes_read < 0)
{
fprintf (stderr, "error reading from master pty: %s\n", strerror (errno));
exit_code = EXIT_FAILURE;
}
fclose (lf);
if (slavefd >= 0)
close (slavefd);
if (masterfd >= 0)
{
removeLineFromUtmp (slave_name, masterfd);
close (masterfd);
}
exit (exit_code);
}
The problem is now that it only works for reading the first message, then read gives me a EIO error. Why is that?
It looks like this happens simply when the last slave file descriptor is closed. Considering write(1) and wall(1) will have the only file descriptor to the slave, you get EIO as soon as those finish writing.
The easiest way to keep this from happening is by keeping a file descriptor around. Right after your ptsname call, do an open(slave_name, O_RDRW).
(Curiously, you already have a slavefd variable, and the code to clean it up. Are you testing us? :p)

Resources