Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux reboot function called in C program causes file loss created by the program on disk

Tags:

c

linux

reboot

I have developped a C program (Linux), this program create a new file and write into, after that it reboots the PC.

After reboot, I have lost the file created by my program. When I deactivate reboot function, the file created by my program is still present.

This behaviour is seen with Linux: - OpenWrt (Backfire 10.03) on VirtualBox (filesystem ext2) - Linux (Ubuntu) (filesystem ext4)

Have you an explication for this behavior and how can I fix it?

#include <stdio.h>
#include <sys/reboot.h>

int main ()
{
    FILE    *pFile;
    char    mybuffer[80];

    pFile = fopen ("/home/user/Desktop/example.txt","w");
    if (pFile == NULL) perror ("Error opening file");
    else
    {
        fputs ("test",pFile);
        fclose (pFile);
    }
    rename("/home/user/Desktop/example.txt","/home/user/Desktop/example123.txt");
    reboot(RB_AUTOBOOT);
    return 0;
}
like image 786
developer Avatar asked May 14 '12 14:05

developer


2 Answers

The man page for fclose says:

Note that fclose() only flushes the user space buffers provided by the C library. To ensure that the data is physically stored on disk the kernel buffers must be flushed too, for example, with sync(2) or fsync(2).

Which means that you need to call fsync before closing the file descriptor.

like image 62
pmr Avatar answered Sep 22 '22 00:09

pmr


The immediate problem is, that you don't sync the file before doing the reboot. The actual problem is, that you call the reboot syscall directly, without regard for what else is happening on the system. What you do is very similar to simply pressing the HW reset button; you just give the kernel the chance to do a little bit of cleanup, but then everything is killed the hard way. This is a dead sure way to eventually corrupt filesystems and file structures. Don't do this!.

Instead you should ask the init system to perform a gracefull reboot. Calling the reboot syscall requires privileged access. So you can just ask the init system to reboot as well. On most systems there's a symlink /sbin/reboot that points to the program that will initiate a sane reboot if called through that symlink. Hence I recommend you replace your dirty reboot(RB_AUTOBOOT) with (note the double specification of "/sbin/reboot" in execlp – this is important).

pid_t reboot_pid;
if( 0 == (reboot_pid = fork()) ) {
    execlp("/sbin/reboot", "/sbin/reboot", NULL);
    exit(1); /* never reached if execlp succeeds. */
}
if( -1 == reboot_pid ) {
    /* fork error... deal with it somehow */
}
int reboot_status;
waitpid(reboot_pid, &reboot_status, 0);
if( !WIFEXITED(reboot_status) ) {
    /* reboot process did not exit sanely... deal with it somehow */
}
if( 0 != WIFEXITSTATUS(reboot_status) ) {
    /* reboot process exited with error;
     * most likely the user lacks the required privileges */
}
else {
    fputs("reboot call sucessfull -- system is about to shutdown.");
    /* The init system is now shutting down the system. It will signals all
     * programs to terminate by sending SIGTERM, followed by SIGKILL to
     * programs that didn't terminate gracefully. */
}

Doing it that way the system can shut down gracefully, terminate all programs running in a clean way and unmount all filesystems before doing the reboot, thereby keeing filesystem and data integrity.

Note that if you expect your program not to have root access, then you'll have to jump some hoops; on systems with systemd you can send a reboot request by D-Bus. But except it to fail, if the user executing the command does not have reboot privileges.

like image 40
datenwolf Avatar answered Sep 24 '22 00:09

datenwolf