/*-
* 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