/*-
 * Copyright (C) @BABOLO V.M 0.2 ΟΤ 2000 May 25
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *
 * $FreeBSD:  $ 
 */

#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sysexits.h>
#include <sys/uio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <err.h>

#define MY_ID 0x1984e34f8969adfb
#define VERSION 1

#ifndef NAMEPID
#define NAMEPID "+deleted.pid="
#endif  NAMEPID

#define BONUS_LIFE 2  /* 1 second minimum for rounding */
#define USE_A 1
#define USE_M 2
#define USE_C 4
#define NOW (time(NULL))
#define SHOW_DIR_DEBUG_LEVEL 3
#define C_PID_LEN 8
enum bool {undef=-1, false=0, true=1};

struct deleted_pid
{   char cpid[C_PID_LEN];
    long long id;
    int version;
    pid_t pid;
    int prio;
    time_t ttl;
    time_t wake_up;
    unsigned char fmask, dmask;
};

struct d_entry
{   struct d_entry *next;
    struct d_entry *high;
    char *name;
    time_t time;
};

char *myname;
short flag_v;
enum bool res_low;

#define RES_LOW(A) \
{   if  (flag_v > 1) fprintf(stderr, "Resources low in ###%03d\n", (A));\
    res_low = true;\
    break;\
}

void debug_date(time_t time)
{   struct tm *t;
    const char mn[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun"
                         , "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
                         };

    t = localtime(&time);
    fprintf( stderr, "%04d-%s-%02d %02d:%02d:%02d"
           , 1900 + t->tm_year, mn[t->tm_mon], t->tm_mday
           , t->tm_hour, t->tm_min, t->tm_sec
           );
}

void debug_time(time_t time)
{   time_t tim = time;
    unsigned t;

    for (t = 0; tim >= 31556952; t++) tim -= 31556952;
    if  (t) fprintf(stderr, "%uy", t);
    for (t = 0; tim >= 604800; t++) tim -= 604800;
    if  (t) fprintf(stderr, "%uw", t);
    for (t = 0; tim >= 86400; t++) tim -= 86400;
    if  (t) fprintf(stderr, "%ud", t);
    for (t = 0; tim >= 3600; t++) tim -= 3600;
    if  (t) fprintf(stderr, "%uh", t);
    for (t = 0; tim >= 60; t++) tim -= 60;
    if  (t) fprintf(stderr, "%um", t);
    if  (tim || !time) fprintf(stderr, "%us", (unsigned)tim);
}

void debug_mask(int mask)
{   enum bool defined;

    defined = false;
    if  (mask & USE_A)
    {   fprintf(stderr, " atime");
        defined = true;
    }
    if  (mask & USE_C)
    {   fprintf(stderr, " ctime");
        defined = true;
    }
    if  (mask & USE_M)
    {   fprintf(stderr, " mtime");
        defined = true;
    }
    if  (!defined) fprintf(stderr, " empty");
}

void debug_node(struct d_entry *l)
{   if  (l)
    {   debug_date(l->time);
        fprintf( stderr, " %08X next %08X high %08X %s\n"
               , (unsigned int)l, (unsigned int)(l->next), (unsigned int)(l->high), l->name
               );
    }else fprintf( stderr, "-----------------\n");
}

void debug_tree(struct d_entry *tree, int level)
{   int i;
    struct d_entry *l;

    for (l = tree; l; l = l->next)
    {   for (i = 0; i < level; i++) fputc('|', stderr);
        debug_node(l);
        if  (l->high) debug_tree(l->high, level + 1);
}   }

time_t select_time(struct stat *sb, int mask)
{   enum bool defined;
    time_t result = 0;

    defined = false;
    if  (mask & USE_A)
    {   result = sb->st_atime;
        defined = true;
    }
    if  (mask & USE_C)
    {   if  (!defined) result = sb->st_ctime;
        else if (result < sb->st_ctime) result = sb->st_ctime;
        defined = true;
    }
    if  (mask & USE_M)
    {   if  (!defined) result = sb->st_mtime;
        else if (result < sb->st_mtime) result = sb->st_mtime;
        defined = true;
    }
    if  (defined) return(result);
    fprintf(stderr, "%s: time mask is empty\n", myname);
    exit(EX_USAGE);
}

void free_node(struct d_entry *node)
{   if  (node->name) free(node->name);
    free(node);
}

void free_tree(struct d_entry *tree)
{   struct d_entry *d;

    while (tree)
    {   if  (tree->high && tree->next)
        {   d = tree->next;
            if  (d->high && d->next)
            {   tree->next = d->high;
                d->high = tree;
                tree = d;
            }else if  (d->high)
            {   tree->next = d->high;
                free_node(d);
            }else if  (d->next)
            {   tree->next = d->next;
                free_node(d);
            }else
            {   tree->next = NULL;
                free_node(d);
            }
        }else if  (tree->high)
        {   d = tree;
            tree = tree->high;
            free_node(d);
        }else if  (tree->next)
        {   d = tree;
            tree = tree->next;
            free_node(d);
        }else
        {   free_node(tree);
            tree = NULL;
}   }   }

#define FREE_TREE(A) {free_tree(A); A = NULL;}

time_t youngest(time_t *a, time_t b)
{   if  (*a < b) *a = b;
    return(*a);
}

char *high_name(const char *path, const char *next)
{   char *next_name;

    asprintf( &next_name, "%s%s%s"
            , path
            , ( *path && path[strlen(path) - 1] == '/') ? "" : "/"
            , next
            );
    return(next_name);
}

enum bool isin_tree(struct d_entry **list, const char *name)
{   struct d_entry *d;

    if  (list) for (d = *list; d; d = d->next) if (!strcmp(name, d->name)) return(true);
    return(false);
}

struct d_entry *create_n(const char *name, time_t time)
{   struct d_entry *d;

    d = (struct d_entry *)malloc(sizeof(struct d_entry));
    if  (d)
    {   d->high = NULL;
        d->next = NULL;
        d->name = name ? strdup(name) : NULL;
        d->time = time;
    }
    return(d);
}

void delete_n(struct d_entry **list)
{   struct d_entry *d;

    d = *list;
    *list = d->next;
    d->next = NULL;
    FREE_TREE(d);
}

void insert_n(struct d_entry **list, struct d_entry *d)
{   struct d_entry **l;
    int i;

    for (i = 0, l = list; *l && (*l)->time < d->time; l = &((*l)->next)) i++;
    if  (i) FREE_TREE(d->high);
    if  (*l) FREE_TREE((*l)->high);
    d->next = *l;
    *l = d;
}

int bubble_n(struct d_entry **list)
{   struct d_entry *d, **l;
    int i;

    i = 0;
    if  (list && *list && (*list)->next)
    {   d = *list;
        for (l = list; *(l = &((*l)->next)) && (*l)->time < d->time;) i++;
        if  (i)
        {   FREE_TREE(d->high);
            *list = d->next;
            d->next = *l;
            *l = d;
    }   }
    return(i);
}

/***********************************************************************
 *                                                                     */
struct d_entry *build_tree(struct d_entry **list, const char *path, int fmask, int dmask)
/*                                                                     *
 ***********************************************************************
 * Build partial tree
 *              high       high       high       high
 * tree -> node ----> node ----> node ----> node ----> node  < the oldest node in the tree
 *          |          |          |                     |
 *          | next     | next     | next                | next
 *          V          V          V                     V
 *         node       node       node                  node
 *          |          |          |                     |
 *          | next     | next     | next                | next
 *          V          V          V                     V
 *         node       node       node                  node
 *          |          |                                |
 *          | next     | next   sorted                  | next
 *          V          V        by time                 V
 *         node       node                             node
 *
 *        sorted     sorted                           sorted
 *        by time    by time                          by time - oldest is highest,
 *                                                              younger is lower,
 *                                                              bubbles go down (in sort)
 *
 * Next link points to the node in the same directory as parent.
 * Next linked list sorted by time.
 * High link points to subdirectory.
 * The only high linked line exists - the way to the
 * oldest node in the directory.
 * Time of the directory is oldest time of files and subdirectories
 * of this directory if it is not empty.
 **********************************************************************
 */

{   DIR *dirp;
    struct dirent *dp;
    struct d_entry *d, *l;
    struct stat sb;
    size_t s;
    char *next_name;
    int f;
    enum bool rebild;

    if  (flag_v > 1) fprintf(stderr, "Build tree %s\n", path);
    if  (   lstat(path, &sb)
        ||  !S_ISDIR(sb.st_mode)
        ||  !(dirp = opendir(path))
        )   return(NULL);
    if  (!list)
    {   rebild = false;
        l = NULL;
        list = &l;
    }else rebild = true;
    s = strlen(NAMEPID);
    while ((dp = readdir(dirp)) != NULL)
    {   if  (   strcmp(dp->d_name, ".")
            &&  strcmp(dp->d_name, "..")
            &&  (   dp->d_namlen != s
                ||  strcmp(path, "/")
                ||  strcmp(dp->d_name, NAMEPID)
            )   )
        {   chdir(path);
            if  (   (rebild && isin_tree(list, dp->d_name))
                ||  lstat(dp->d_name, &sb)
                );
            else if  (S_ISLNK(sb.st_mode))
            {   time_t linktime = select_time(&sb, fmask);
                if  (!stat(dp->d_name, &sb)) (void)youngest(&linktime, select_time(&sb, fmask));
                if  (!(d = create_n(dp->d_name, linktime))) RES_LOW(600);                       /*###*/
                insert_n(list, d);
            }else if  (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)))
            {   if  (!(d = create_n(dp->d_name, select_time(&sb, fmask)))) RES_LOW(601);        /*###*/
                insert_n(list, d);
            }else if  ((f = open(dp->d_name, O_RDONLY | O_EXLOCK | O_NONBLOCK)) >= 0)
            {   close(f);
                if  (S_ISREG(sb.st_mode))
                {   if  (!(d = create_n(dp->d_name, select_time(&sb, fmask)))) RES_LOW(602);    /*###*/
                    insert_n(list, d);
                }else if  (S_ISDIR(sb.st_mode))
                {   if  (!(d = create_n(dp->d_name, select_time(&sb, dmask)))) RES_LOW(603);    /*###*/
                    if  (!(next_name = high_name(path, dp->d_name))) RES_LOW(604);              /*###*/
                    d->high = build_tree(NULL, next_name, fmask, dmask);
                    free(next_name);
                    if  (d->high) d->time = d->high->time;
                    insert_n(list, d);
                }
            }else if (errno == EMFILE || errno == ENFILE)
            {   RES_LOW(605);                                                                   /*###*/
            }else if (errno == ETXTBSY || errno == EAGAIN)
            {   if  (!(d = create_n(dp->d_name, NOW))) RES_LOW(606);                            /*###*/
                insert_n(list, d);
    }   }   }
    (void)closedir(dirp);
    return(*list);
}

void clear_tree(struct d_entry **tree, time_t when, const char *path, int fmask, int dmask)
{   struct stat sb;
    int f;
    char *next_name = NULL;
#define REBILD(TREE, CODE) \
{   if  (*(TREE))\
    {   if  (!(next_name = high_name(path, (*(TREE))->name))) RES_LOW(CODE);\
        (void)build_tree(&(*(TREE))->high, next_name, fmask, dmask);\
        free(next_name);\
}   }

    if  (flag_v > 1) fprintf(stderr, "Clear tree %s\n", path);
    if  (flag_v > SHOW_DIR_DEBUG_LEVEL + 1) debug_tree(*tree, 0);
    for (; *tree && (*tree)->time <= when;)
    {   chdir(path);
        if  (lstat((*tree)->name, &sb))
        {   if  (flag_v) fprintf(stderr, "     gone     %s\n", (*tree)->name);
            delete_n(tree);
            REBILD(tree, 700);                                                                  /*###*/
        }else if  (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)))
        {   time_t linktime = select_time(&sb, fmask);
            if  (S_ISLNK(sb.st_mode) && !stat((*tree)->name, &sb))
                (void)youngest(&linktime, select_time(&sb, fmask));
            if  (youngest(&((*tree)->time), linktime) <= when)
            {   if  (unlink((*tree)->name))
                {   if  (flag_v) fprintf(stderr, "no unlink XXX %s\n", (*tree)->name);
                    (*tree)->time = NOW;
                    if  (bubble_n(tree)) REBILD(tree, 701);                                     /*###*/
                }else
                {   if  (flag_v) fprintf(stderr, "   unlink XXX %s\n", (*tree)->name);
                    delete_n(tree);
                    REBILD(tree, 702);                                                          /*###*/
                }
            }else
            {   if  (flag_v) fprintf(stderr, "  younger XXX %s\n", (*tree)->name);
                if  (bubble_n(tree)) REBILD(tree, 703);                                         /*###*/
            }
        }else if ((f = open((*tree)->name, O_RDONLY | O_EXLOCK | O_NONBLOCK)) >= 0)
        {   close(f);
            if  (S_ISREG(sb.st_mode))
            {   if  (youngest(&((*tree)->time), select_time(&sb, fmask)) <= when)
                {   if  (unlink((*tree)->name))
                    {   if  (flag_v) fprintf(stderr, "no unlink REG %s\n", (*tree)->name);
                        (*tree)->time = NOW;
                        if  (bubble_n(tree)) REBILD(tree, 704);                                 /*###*/
                    }else
                    {   if  (flag_v) fprintf(stderr, "   unlink REG %s\n", (*tree)->name);
                        delete_n(tree);
                        REBILD(tree, 705);                                                      /*###*/
                    }
                }else
                {   if  (flag_v) fprintf(stderr, "  younger REG %s\n", (*tree)->name);
                    if  (bubble_n(tree)) REBILD(tree, 706);                                     /*###*/
                }
            }else if  (S_ISDIR(sb.st_mode) && (*tree)->time <= when)
            {   if  ((*tree)->high)
                {   if  (!(next_name = high_name(path, (*tree)->name))) RES_LOW(707);           /*###*/
                    clear_tree(&((*tree)->high), when, next_name, fmask, dmask);
                    free(next_name);
                    chdir(path);
                    if  ((*tree)->high) (*tree)->time = (*tree)->high->time;
                    else if(!stat((*tree)->name, &sb))
                        (void)youngest(&((*tree)->time), select_time(&sb, dmask));
                    if  (bubble_n(tree)) REBILD(tree, 708);                                     /*###*/
                }else if (!rmdir((*tree)->name))
                {   if  (flag_v) fprintf(stderr, "    rmdir DIR %s\n", (*tree)->name);
                    delete_n(tree);
                    REBILD(tree, 709);                                                          /*###*/
                }else
                {   if  (flag_v) fprintf(stderr, "no  rmdir DIR %s\n", (*tree)->name);
                    (*tree)->time = NOW;
                    if  (bubble_n(tree)) REBILD(tree, 710);                                     /*###*/
                }
            }
        }else if (errno == EMFILE || errno == ENFILE)
        {   RES_LOW(711);                                                                       /*###*/
        }else if (errno == ETXTBSY || errno == EAGAIN)
        {   if  (flag_v) fprintf(stderr, " lock younger %s\n", (*tree)->name);
            (*tree)->time = NOW;
            if  (bubble_n(tree)) REBILD(tree, 712);                                             /*###*/
    }   }
    (void)build_tree(tree, path, fmask, dmask);
    while (0 && *tree && !((*tree)->high))
    {   if  (!(next_name = high_name(path, (*tree)->name))) RES_LOW(713);                       /*###*/
        (void)build_tree(&(*tree)->high, next_name, fmask, dmask);
        free(next_name);
        if  (!bubble_n(tree)) break;
    }
    if  (flag_v > 1) fprintf(stderr, "Cleared    %s\n", path);
}

int prio_set(int conf_prio)
{   int prio;
    enum bool correct;

    correct = true;
    errno = 0;
    prio = getpriority(PRIO_PROCESS, 0);
    if  (prio == -1 && errno)
    {   if  (flag_v) warn("getpriority");
        prio = 0;
        correct = false;
    }
    if  (!correct || prio < conf_prio)
    {   (void)setpriority(PRIO_PROCESS, 0, conf_prio);
        errno = 0;
        prio = getpriority(PRIO_PROCESS, 0);
        if  (prio == -1 && errno)
        {   if  (flag_v) warn("getpriority");
            prio = 0;
    }   }
    return(prio);
}

void main_loop(struct deleted_pid *conf, int prio)
{   struct d_entry *tree;
    time_t now, base, unsleep, ttl;

    tree = build_tree(NULL, "/", conf->fmask, conf->dmask);
    if  (flag_v == SHOW_DIR_DEBUG_LEVEL) debug_tree(tree, 0);
    base = NOW;
    while (prio <= conf->prio)
    {   if  (flag_v > SHOW_DIR_DEBUG_LEVEL) debug_tree(tree, 0);
        res_low = false;                              /*XXXX need res_low test */
        ttl = conf->ttl + BONUS_LIFE;
        now = NOW;
        if  (tree) conf->wake_up = ttl + tree->time;
          else    conf->wake_up = ttl + base;
        if  (flag_v)
        {   fprintf(stderr, "UTC   now ");
            debug_date(now);
            if  (conf->wake_up > now)
            {   fprintf(stderr, ",    wake_up at ");
                debug_date(conf->wake_up);
            }
            fprintf(stderr, "\n");
        }
        unsleep = 0;
        if  (conf->wake_up > now) unsleep = sleep(conf->wake_up - now);
        if  (!unsleep)
        {   clear_tree(&tree, NOW - ttl, "/", conf->fmask, conf->dmask);
            base = NOW;
        }
        prio = prio_set(conf->prio);
}   }

void query()
{   int file_pid;
    struct deleted_pid *conf;
    pid_t pid;

    conf = NULL;
    file_pid = open(NAMEPID, O_RDONLY | O_NONBLOCK | O_EXLOCK, 0600);
    if  (file_pid >= 0)                                           /* If no other deleted running */
    {   fprintf(stderr, "Control file found. Deleted daemon not found\n");
        if  (flock(file_pid, LOCK_SH))             err(EX_UNAVAILABLE, "flock shared after exclusive");
    }else if (errno != EWOULDBLOCK) fprintf(stderr, "Control file not found or not permitted\n");
    else
    {   file_pid = open(NAMEPID, O_RDONLY | O_NONBLOCK | O_SHLOCK);
        if  (file_pid >= 0) fprintf(stderr, "Control file found.\n");
          else fprintf(stderr, "Where is control file?\n");
    }
    if  (file_pid >= 0)
    {   conf = (struct deleted_pid *)mmap( NULL
                                         , sizeof(struct deleted_pid)
                                         , PROT_READ
                                         , MAP_SHARED
                                         , file_pid
                                         , (off_t)0
                                         );
        if  ((void*)conf == MAP_FAILED)                                    err(EX_UNAVAILABLE, "mmap");
        if  (   conf->id != MY_ID
            ||  conf->version != VERSION
            )
        {   fprintf(stderr, NAMEPID" belongs to another program or version\n");
        }else
        {   pid = atoi(conf->cpid);
            if  (pid != conf->pid)
                fprintf(stderr, "!!! (cpid == %d) != (pid == %d) !!!\n", pid, conf->pid);
            else
            {   fprintf(stderr, "pid == %d", pid);
                if  (kill(pid, 0)) fprintf(stderr, " not running\n");
                  else fprintf(stderr, " running\n");
            }
            fprintf( stderr, "Nice = %d, TimeToLive = ", conf->prio);
            debug_time(conf->ttl);
            fprintf( stderr, "\nWake up at ");
            debug_date(conf->wake_up);
            fprintf(stderr, "\nFile      mask:");
            debug_mask(conf->fmask);
            fprintf(stderr, "\nDirectory mask:");
            debug_mask(conf->dmask);
            fprintf(stderr, "\n");
}   }   }

void usage()
{   fprintf( stderr
           , "deleted @BABOLO V.M "VERS" ΟΤ 2000 May 30\n"
             "Usage:\n"
             "deleted -t TimeToLive [-n Nice] [-f mask] [-d mask] directory\n"
             "    where mask is some word from [amc]*\n"
             "    for files (-f) and directories (-d),\n"
             "        a - use atime\n"
             "        m - use mtime\n"
             "        c - use ctime\n"
             " or\n"
             "deleted -q directory\n"
             "    to query running deleted daemon\n"
           );
}

int get_t_mask(char *in)
{   char *p;
    int res;

    for (res = 0, p = in; p && *p; p++)
    {   switch (*p)
        {case 'a': res |= USE_A; break;
         case 'm': res |= USE_M; break;
         case 'c': res |= USE_C; break;
         default:  usage()     ; exit(EX_USAGE);
    }   }
    return(res);
}

void huphandler(int signal) {return;}

int main(int argc, char **argv)
{   int c, flag_q;
    struct deleted_pid parm;
    int file_pid;
    int prio;
    struct sigaction act = {{huphandler}, 0, {{0}}};
    struct deleted_pid *conf;

    myname = *argv;
    flag_v = 0;
    flag_q = 0;
    parm.id = MY_ID;
    parm.version = VERSION;
    parm.pid = getpid();
    parm.ttl = 0;
    parm.prio = 0;
    bzero(parm.cpid, C_PID_LEN);
    snprintf(parm.cpid, C_PID_LEN, "%d\n", parm.pid);
    parm.fmask = USE_A | USE_M | USE_C;
    parm.dmask = USE_M | USE_C;
    while ((c = getopt(argc, argv, "qvn:t:d:f:h?")) > 0)
    {   time_t t;

        switch (c)
        {case 'q': flag_q++;                                 break;
         case 'v': flag_v++;                                 break;
         case 't':
            for (parm.ttl = 0, t = 0; *optarg; optarg++)
            {   switch (*optarg)
                {case '0': case '1': case '2': case '3': case '4':
                 case '5': case '6': case '7': case '8': case '9':
                    t = t * 10 + (*optarg - '0');         break;
                 case 'Y':case 'y': t *= 53;
                 case 'W':case 'w': t *= 7;
                 case 'D':case 'd': t *= 24;
                 case 'H':case 'h': t *= 60;
                 case 'M':case 'm': t *= 60;
                 case 'S':case 's': parm.ttl += t; t = 0; break;
                 default: while (*optarg) optarg++;
                }
            }   parm.ttl += t;                               break;
         case 'n': parm.prio = atoi(optarg);                 break;
         case 'd': parm.dmask = get_t_mask(optarg);          break;
         case 'f': parm.fmask = get_t_mask(optarg);          break;
         case 'h':
         case '?':
         default: usage();                                   exit(EX_OK);
    }   }

    argc -= optind;
    argv += optind;
    if  (!parm.ttl && !flag_q)
    {   fprintf(stderr, "TimeToLive must be specified\n");
        usage();
        exit(EX_USAGE);
    }
    if  (argc != 1)
    {   fprintf(stderr, "TmpDir must be specified\n");
        usage(EX_USAGE);
        exit(EX_USAGE);
    }



    /* *argv may be absolute path. or relative... */
    if  (!flag_q)
    {   /**************************/
        /* it is what seteuid for */
        if  (chroot(*argv))                                              err(EX_UNAVAILABLE, "chroot");
    }   /*                        */
        /*                        */
        /*                        */
    if  (setuid(getuid()))                                               err(EX_UNAVAILABLE, "setuid");
        /*     END seteuid bit    */
        /**************************/



    if  (flag_q)
    {   if  (chdir(*argv))                                                err(EX_UNAVAILABLE, "chdir");
        query();
        exit(EX_OK);
    }else if (chdir("/"))                                               err(EX_UNAVAILABLE, "chdir /");
    if  (sigaction(SIGHUP, &act, NULL))                               err(EX_UNAVAILABLE, "sigaction");
    prio = prio_set(parm.prio);
    if  (parm.prio < prio)
    {   if  (flag_v) fprintf(stderr, "Nice %d impossible, %d used instead.\n", parm.prio, prio);
        parm.prio = prio;
    }
    conf = NULL;
    file_pid = -1;
    /* If two or more deleted starts almoust simultaniesly for this dir, one loop can be unsuccessful*/
    while (1)
    {   file_pid = open(NAMEPID, O_RDWR | O_NONBLOCK | O_CREAT | O_EXLOCK, 0600);
        if  (file_pid >= 0)                                           /* If no other deleted running */
        {   struct stat sb, sbf;

            if  (flag_v) fprintf(stderr, "First deleted started\n");
            if  (   (lstat(NAMEPID, &sb))
                ||  (fstat(file_pid, &sbf))
                )                                                      err(EX_UNAVAILABLE, "[fl]stat");
            if  (   (!S_ISREG(sb.st_mode))
                ||  (sb.st_dev != sbf.st_dev)
                ||  (sb.st_ino != sbf.st_ino)
                )
            {   fprintf(stderr, "!!! Substitute attack !!! -> "NAMEPID);
                exit(EX_TEMPFAIL);
            }
            if  (ftruncate(file_pid, sizeof(struct deleted_pid)))     err(EX_UNAVAILABLE, "ftruncate");
            if  (write(file_pid, &parm, sizeof(struct deleted_pid)) != sizeof(struct deleted_pid))
                                                                    err(EX_UNAVAILABLE, "First write");
            if  (flock(file_pid, LOCK_SH))         err(EX_UNAVAILABLE, "flock shared after exclusive");
            conf = (struct deleted_pid *)mmap( NULL
                                             , sizeof(struct deleted_pid)
                                             , PROT_READ | PROT_WRITE
                                             , MAP_SHARED
                                             , file_pid
                                             , (off_t)0
                                             );
            if  ((void*)conf == MAP_FAILED)                                err(EX_UNAVAILABLE, "mmap");
            main_loop(conf, prio);
            break;
        }
        if  (errno != EWOULDBLOCK)                                 err(EX_UNAVAILABLE, "open "NAMEPID);
        file_pid = open(NAMEPID, O_RDWR | O_NONBLOCK | O_SHLOCK);
        if  (file_pid >= 0)                           /* Change parameters if other deleted running */
        {   conf = (struct deleted_pid *)mmap( NULL
                                             , sizeof(struct deleted_pid)
                                             , PROT_READ | PROT_WRITE
                                             , MAP_SHARED
                                             , file_pid
                                             , (off_t)0
                                             );
            if  ((void*)conf == MAP_FAILED)                                err(EX_UNAVAILABLE, "mmap");
            if  (   conf->id != parm.id
                ||  conf->version != parm.version
                )
            {   fprintf(stderr, NAMEPID" belongs to another program or version\n");
                exit(EX_DATAERR);
            }
            conf->ttl = parm.ttl;
            if  (conf->prio > parm.prio)
            {   if  (flag_v) fprintf(stderr, "Second deleted substitutes\n");
                conf->prio = parm.prio;
                if  (kill(conf->pid, SIGHUP) && errno != ESRCH && errno != EPERM)
                                                                           err(EX_UNAVAILABLE, "kill");
            }else
            {   if  (flag_v) fprintf(stderr, "Second deleted corrects\n");
                conf->prio = parm.prio;
                if  (!kill(conf->pid, SIGHUP)) break;
                else if (errno != ESRCH && errno != EPERM)                 err(EX_UNAVAILABLE, "kill");
            }
        }else if  (errno != EWOULDBLOCK)                           err(EX_UNAVAILABLE, "open "NAMEPID);
        if  (conf) (void)munmap(conf, sizeof(struct deleted_pid));
        conf = NULL;
        if  (file_pid >= 0) (void)flock(file_pid, LOCK_UN);
        file_pid = -1;
        if  (flag_v) fprintf(stderr, "deleted: Next try \n");
        (void)sleep(1);
    }
    if  (conf) (void)munmap(conf, sizeof(struct deleted_pid));
    if  (file_pid >= 0) (void)flock(file_pid, LOCK_UN);
    exit(EX_OK);
}


syntax highlighted by Code2HTML, v. 0.9.1