Professional Web Applications Themes

race condition? - UNIX Programming

I am trying to write logging code for my http server and I decided to make a more abstract logging facility, instead of building it into the server (so that I'd be able to use it on another project very easily). Basically, I want the program that is using it to do: pid_t logger_pid; logger_pid = logger_init("/path/to/logfile"); .... logit(LOG_FATAL, "I died!"); .... logit(LOG_DEBUG, "put debug msg here"); logger_kill(); logger_init() forks a child, then calls logger_main() who opens the logfile then creates a FIFO and opens it for reading. when logit() is called, it opens the FIFO for writing, then sends ...

  1. #1

    Default race condition?

    I am trying to write logging code for my http server and I decided to
    make a more abstract logging facility, instead of building it into the
    server (so that I'd be able to use it on another project very easily).

    Basically, I want the program that is using it to do:

    pid_t logger_pid;

    logger_pid = logger_init("/path/to/logfile");
    ....
    logit(LOG_FATAL, "I died!");
    ....
    logit(LOG_DEBUG, "put debug msg here");
    logger_kill();

    logger_init() forks a child, then calls logger_main() who opens the
    logfile then creates a FIFO and opens it for reading.

    when logit() is called, it opens the FIFO for writing, then sends the
    logger process a string

    back in logger_main() it reads the string and writes it to the logfile.

    When running a test program I made (very similar to above code), logit()
    says it couldn't open the FIFO (no such file or directory).
    Obviously, it is trying to write to it before logger_main() is done
    creating it. Is this what a race condition is?

    I tried putting in a 'logger_ready' flag, where logit() sleep()s for 1
    second if logger_ready == FALSE, and I set logger_ready to TRUE once
    mkfifo() has returned successfully, but it seems like the logger_ready
    flag never gets set, because it never exits the while(!logger_ready) loop.

    I've posted the relevant code below.
    Anyone know what the problem could be?

    Sorry for the long post.
    Thanks,
    Aaron

    --
    /usr/bin/fortune says:
    Some husbands are living proof that a woman can take a joke.

    --------

    static unsigned short logger_ready;
    static sig_atomic_t logger_exit;

    static int log_fd;
    static int fifo_fd;
    static pid_t logger_pid;

    static void logger_exit_handler(int);
    static void logger_main(const char *);
    static void logger_err(const char *, ...);

    pid_t
    logger_init(const char *logfile)
    {
    switch(logger_pid = fork())
    {
    case -1:
    logger_err("fork");
    case 0:
    logger_main(logfile);
    default:
    return logger_pid;
    }
    }

    void
    logit(int priority, const char *msg)
    {
    int fd;
    char pid[10];
    char *buf;
    char *priorities[] = {
    " ** FATAL ** ",
    " ERROR: ",
    " WARNING: ",
    " INFO: ",
    " DEBUG: "
    };

    assert(priority >= 0 && priority <= 4);

    /* save calling process' pid as a string */
    snprintf(pid, sizeof(pid)-1, "[%ld]", (long)getpid());

    /* make sure logger_main() has created our FIFO */
    while(!logger_ready)
    sleep(1);

    fd = open("logger_fifo", O_WRONLY);
    if(fd < 0)
    logger_err("could not open FIFO (logger_fifo) for writing");

    /* prepare log message */
    if(get_progname()) {
    buf = ec_malloc(strlen(get_progname()) + strlen(pid) +
    strlen(": ") + 1);
    strcat(buf, get_progname());
    strcat(buf, pid);
    strcat(buf, ": ");
    }
    else {
    buf = ec_malloc(strlen(pid) + strlen(": ") + 1);
    strcat(buf, pid);
    strcat(buf, ": ");
    }

    switch(priority)
    {
    case LOG_FATAL:
    buf = ec_realloc(buf, strlen(buf) +
    strlen(priorities[LOG_FATAL]) + strlen(msg) + 1);
    strcat(buf, priorities[LOG_FATAL]);
    strcat(buf, msg);
    break;

    case LOG_ERR:
    buf = ec_realloc(buf, strlen(buf) + strlen(priorities[LOG_ERR]) +
    strlen(msg) + 1);
    strcat(buf, priorities[LOG_ERR]);
    strcat(buf, msg);
    break;

    case LOG_WARN:
    buf = ec_realloc(buf, strlen(buf) + strlen(priorities[LOG_WARN]) +
    strlen(msg) + 1);
    strcat(buf, priorities[LOG_WARN]);
    strcat(buf, msg);
    break;

    case LOG_INFO:
    buf = ec_realloc(buf, strlen(buf) + strlen(priorities[LOG_INFO]) +
    strlen(msg) + 1);
    strcat(buf, priorities[LOG_INFO]);
    strcat(buf, msg);
    break;

    case LOG_DEBUG:
    buf = ec_realloc(buf, strlen(buf) + strlen(priorities[LOG_DEBUG]) +
    strlen(msg) + 1);
    strcat(buf, priorities[LOG_DEBUG]);
    strcat(buf, msg);
    break;
    default:
    abort();
    }

    assert(buf);

    strcat(buf, "\n");

    if(write(fd, buf, PIPE_BUF) < 0)
    logger_err("write: logger_fifo");
    close(fd);
    free(buf);
    }

    static void
    logger_main(const char *logfile)
    {
    struct sigaction sigterm;
    mode_t fifo_perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
    S_IROTH | S_IWOTH;
    mode_t log_perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
    S_IROTH;

    /* SIGTERM */
    memset(&sigterm, 0, sizeof(sigterm));
    sigterm.sa_handler = &logger_exit_handler;
    sigaction(SIGTERM, &sigterm, NULL);

    log_fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, log_perms);
    if(log_fd < 0)
    logger_err("couldnt open log_fd (%s)", logfile);

    if(mkfifo("logger_fifo", fifo_perms) < 0) {
    fprintf(stderr, "mkfifo: %s\n", strerror(errno));
    close(log_fd);
    exit(1);
    }

    /* make sure we have created the fifo before logit()
    * attempts to write to it
    */
    logger_ready = TRUE;

    fifo_fd = open("logger_fifo", O_RDONLY);
    if(fifo_fd < 0)
    logger_err("could not open FIFO (logger_fifo) for reading");

    /* main logging loop */
    for(;;) {
    int n;
    char buf[PIPE_BUF];

    if(logger_exit)
    break;

    while((n = read(fifo_fd, buf, PIPE_BUF)) > 0) {
    if(write(log_fd, buf, n) != n)
    logger_err("write: %s", logfile);
    }
    }
    printf("logger [%d] exiting...\n", logger_pid);
    _exit(0);
    }

    Aaron Guest

  2. #2

    Default Re: race condition?

    Aaron Walker <rr.com> writes:
     

    Perhaps you should not be reinventing the wheel and use syslog(3) instead?
     

    Yes, that is a fine example of a race condition: since you do not know
    in which order the child and the parent will run, you can not expect
    the child to have finished with the "setup" part of logger_main()
    before the first call to logit().
     

    Of course it doesn't: after the fork(), child and parent address
    spaces are completely separate, and setting logger_ready=TRUE in
    the child has *absolutely no effect* on the value of logger_ready
    in the parent.

    One solution is to mkfifo() before fork()ing. If you do that and
    the parent tries to log something before the child has started
    "listening", the parent will be blocked (in open()) until the child
    is ready.

    Cheers,
    --
    In order to understand recursion you must first understand recursion.
    Remove /-nsp/ for email.
    Paul Guest

  3. #3

    Default Re: race condition?

    Paul Pluzhnikov wrote:
     

    I was under the impression that syslog() didn't allow you to choose
    which logfile you wanted to log to. Am I mistaken?

    Aaron

    --
    /usr/bin/fortune says:
    Nothing so needs reforming as other people's habits.
    -- Mark Twain, "Pudd'nhead Wilson's Calendar"

    Aaron Guest

  4. #4

    Default Re: race condition?

    Aaron Walker <rr.com> writes:
     

    You are: syslog allows system administrator (who is arguably in
    better position to decide than you are) to choose which file (if any)
    to log to.

    Try "man syslog.conf"

    Cheers,
    --
    In order to understand recursion you must first understand recursion.
    Remove /-nsp/ for email.
    Paul Guest

  5. #5

    Default Re: race condition?

    Paul Pluzhnikov <net> writes:
     
    >
    > You are: syslog allows system administrator (who is arguably in
    > better position to decide than you are) to choose which file (if any)
    > to log to.[/ref]

    I'm jumping in here, so I might have missed something about the OP's
    situation. However, I can think of many situations where the actual
    user might rightfully want some program output somewhere other than
    the system-wide logs chosen by the administrator.

    --
    Måns Rullgård
    se
    Måns Guest

Similar Threads

  1. Replies: 5
    Last Post: December 20th, 07:51 PM
  2. Help a newbie create a race track
    By Gianpiero Colagiacomo in forum Macromedia Director 3D
    Replies: 1
    Last Post: August 24th, 11:59 AM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139