Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a file only if it doesn't exist?

I wrote a UNIX daemon (targeting Debian, but it shouldn't matter) and I wanted to provide some way of creating a ".pid" file, (a file which contains the process identifier of the daemon).

I searched for a way of opening a file only if it doesn't exist, but couldn't find one.

Basically, I could do something like:

if (fileexists())
{
  //fail...
}
else
{
  //create it with fopen() or similar
}

But as it stands, this code does not perform the task in a atomic fashion, and doing so would be dangerous, because another process might create the file during my test, and the file creation.

Do you guys have any idea on how to do that?

Thank you.

P.S: Bonus point for a solution which only involves std::streams.

like image 970
ereOn Avatar asked Mar 26 '12 14:03

ereOn


People also ask

Which mode create a new file if file does not exist?

If a file does not exist, append mode creates the file.

How do you create a file in C if it does not exist?

The fopen() function creates the file if it does not exist.

How do you create a file in Python if it does not exist?

If a file does not exist in your system, you can use the open() method to create one. The open() method takes the file path and mode as input and outputs a file object.

Which command from the following can create a file if doesn't exist?

Create File with cat Command If the file doesn't exist, the Linux cat command will create it. Note the redirection operator. Typically, the command displays the contents of test2.


2 Answers

man 2 open:

O_EXCL Ensure that this call creates the file: if this flag is specified in conjunction with O_CREAT, and pathname already exists, then open() will fail. The behavior of O_EXCL is undefined if O_CREAT is not specified.

so, you could call fd = open(name, O_CREAT | O_EXCL, 0644); /* Open() is atomic. (for a reason) */

UPDATE: and you should of course OR one of the O_RDONLY, O_WRONLY, or O_RDWR flags into the flags argument.

like image 127
wildplasser Avatar answered Sep 28 '22 03:09

wildplasser


I learned about proper daemonizing here (back in the day):

  • http://www.enderunix.org/docs/eng/daemon.php

It is a good read. I have since improved the locking code to eliminate race conditions on platforms that allow advisory file locking with specific regions specified.

Here is a relevant snippet from a project that I was involved in:

static int zfsfuse_do_locking(int in_child)
{
    /* Ignores errors since the directory might already exist */
    mkdir(LOCKDIR, 0700);

    if (!in_child)
    {
        ASSERT(lock_fd == -1);
        /*
         * before the fork, we create the file, truncating it, and locking the
         * first byte
         */
        lock_fd = creat(LOCKFILE, S_IRUSR | S_IWUSR);
        if(lock_fd == -1)
            return -1;

        /*
         * only if we /could/ lock all of the file,
         * we shall lock just the first byte; this way
         * we can let the daemon child process lock the
         * remainder of the file after forking
         */
        if (0==lockf(lock_fd, F_TEST, 0))
            return lockf(lock_fd, F_TLOCK, 1);
        else
            return -1;
    } else
    {
        ASSERT(lock_fd != -1);
        /*
         * after the fork, we instead try to lock only the region /after/ the
         * first byte; the file /must/ already exist. Only in this way can we
         * prevent races with locking before or after the daemonization
         */
        lock_fd = open(LOCKFILE, O_WRONLY);
        if(lock_fd == -1)
            return -1;

        ASSERT(-1 == lockf(lock_fd, F_TEST, 0)); /* assert that parent still has the lock on the first byte */
        if (-1 == lseek(lock_fd, 1, SEEK_SET))
        {
            perror("lseek");
            return -1;
        }

        return lockf(lock_fd, F_TLOCK, 0);
    }
}

void do_daemon(const char *pidfile)
{
    chdir("/");
    if (pidfile) {
        struct stat dummy;
        if (0 == stat(pidfile, &dummy)) {
            cmn_err(CE_WARN, "%s already exists; aborting.", pidfile);
            exit(1);
        }
    }

    /*
     * info gleaned from the web, notably
     * http://www.enderunix.org/docs/eng/daemon.php
     *
     * and
     *
     * http://sourceware.org/git/?p=glibc.git;a=blob;f=misc/daemon.c;h=7597ce9996d5fde1c4ba622e7881cf6e821a12b4;hb=HEAD
     */
    {
        int forkres, devnull;

        if(getppid()==1)
            return; /* already a daemon */

        forkres=fork();
        if (forkres<0)
        { /* fork error */
            cmn_err(CE_WARN, "Cannot fork (%s)", strerror(errno));
            exit(1);
        }
        if (forkres>0)
        {
            int i;
            /* parent */
            for (i=getdtablesize();i>=0;--i)
                if ((lock_fd!=i) && (ioctl_fd!=i))       /* except for the lockfile and the comm socket */
                    close(i);                            /* close all descriptors */

            /* allow for airtight lockfile semantics... */
            struct timeval tv;
            tv.tv_sec = 0;
            tv.tv_usec = 200000;  /* 0.2 seconds */
            select(0, NULL, NULL, NULL, &tv);

            VERIFY(0 == close(lock_fd));
            lock_fd == -1;
            exit(0);
        }

        /* child (daemon) continues */
        setsid();                         /* obtain a new process group */
        VERIFY(0 == chdir("/"));          /* change working directory */
        umask(027);                       /* set newly created file permissions */
        devnull=open("/dev/null",O_RDWR); /* handle standard I/O */
        ASSERT(-1 != devnull);
        dup2(devnull, 0); /* stdin  */
        dup2(devnull, 1); /* stdout */
        dup2(devnull, 2); /* stderr */
        if (devnull>2)
            close(devnull);

        /*
         * contrary to recommendation, do _not_ ignore SIGCHLD:
         * it will break exec-ing subprocesses, e.g. for kstat mount and
         * (presumably) nfs sharing!
         *
         * this will lead to really bad performance too
         */
        signal(SIGTSTP,SIG_IGN);     /* ignore tty signals */
        signal(SIGTTOU,SIG_IGN);
        signal(SIGTTIN,SIG_IGN);
    }

    if (0 != zfsfuse_do_locking(1))
    {
        cmn_err(CE_WARN, "Unexpected locking conflict (%s: %s)", strerror(errno), LOCKFILE);
        exit(1);
    }

    if (pidfile) {
        FILE *f = fopen(pidfile, "w");
        if (!f) {
            cmn_err(CE_WARN, "Error opening %s.", pidfile);
            exit(1);
        }
        if (fprintf(f, "%d\n", getpid()) < 0) {
            unlink(pidfile);
            exit(1);
        }
        if (fclose(f) != 0) {
            unlink(pidfile);
            exit(1);
        }
    }
}

See also http://gitweb.zfs-fuse.net/?p=sehe;a=blob;f=src/zfs-fuse/util.c;h=7c9816cc895db4f65b94592eebf96d05cd2c369a;hb=refs/heads/maint

like image 22
sehe Avatar answered Sep 28 '22 03:09

sehe