/*
AND auto nice daemon - renice programs according to their CPU usage.
Copyright (C) 1999-2004 Patrick Schemitz <schemitz@users.sourceforge.net>
http://and.sourceforge.net/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/************************************************************************
* *
* and.c -- AND library for platform-independent code. *
* *
* Automatically renice jobs when they use too much CPU time. *
* *
* 1999-2004 Patrick Schemitz <schemitz@users.sourceforge.net> *
* http://and.sourceforge.net/ *
* *
***********************************************************************/
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <grp.h>
#include <syslog.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <signal.h>
#include <regex.h>
#if defined(__FreeBSD__) && __FreeBSD_version >= 500014
#include <sys/limits.h>
#else
#include <limits.h>
#endif
#define DEBUG 0
/* OpenBSD getopt() is in unistd.h; Linux and Digital UNIX have getopt.h */
#if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__svr4__) && !defined(__SunOS__)
#include <getopt.h>
#endif
/* GNU C library forgets to declare this one: */
#ifdef __GNUC__
int vsnprintf (char *str, size_t n, const char *format, va_list ap);
#define HAVE_VSNPRINTF
#endif
#include "and.h"
#ifndef DEFAULT_NICE
#define DEFAULT_NICE 0
#endif
#ifndef LOG_PERROR
#define LOG_PERROR 0
#endif
#ifndef DEFAULT_INTERVAL
#define DEFAULT_INTERVAL 60
#endif
#ifndef DEFAULT_CONFIG_FILE
#define DEFAULT_CONFIG_FILE "/etc/and.conf"
#endif
#ifndef DEFAULT_DATABASE_FILE
#define DEFAULT_DATABASE_FILE "/etc/and.priorities"
#endif
#ifndef AND_VERSION
#define AND_VERSION "1.0.7 or above (not compiled in)"
#endif
#ifndef AND_DATE
#define AND_DATE "27 Jan 2002 or later (not compiled in)"
#endif
#define bool char
#define false (0)
#define true (!false)
/* Maximum entries in priority database. You may change this,
if you have a really large priority database. However, you
shouldn't make too large databases since the get_priority()
function takes too long otherwise. */
#define PRI_MAXENTRIES 100
/* Indices for the weight array when resolving a user/group/command/parent
tuple to get the correct priority database entry. These are
constants. Never ever change them! Change the affinity in the
config file instead. */
enum {
PRI_U, PRI_G, PRI_C, PRI_P, PRI_N
};
/* Priority database entry record. Consists of the user ID, the
group ID, the command (string and compiled regexp), the parent
(string and compiled regexp), and three nice levels. */
#define PARENT_ONLY 0
#define PARENT_AND_ANCHESTORS 1
#define PARENT_ONLY_KEYWORD "parent="
#define PARENT_AND_ANCHESTORS_KEYWORD "ancestor="
struct priority_db_entry {
int uid;
int gid;
char command_str [128];
regex_t *command;
char parent_str [128];
regex_t *parent;
int parentmode;
int nl [3];
};
/* Global variables for priority database (db), configuration (conf),
and other arguments (args). */
struct{
int n;
struct priority_db_entry entry [PRI_MAXENTRIES];
} and_db;
/* AND configuration data. Some of this is compiled in; for some things
there are command-line options, and some is read from and.conf */
struct {
char hostname [512];
int test;
char *program;
char *config_file;
char *database_file;
int verbose;
int to_stdout;
int nice_default;
bool lock_interval;
unsigned interval;
unsigned time_mark [3];
char affinity [5];
int weight [PRI_N];
int min_uid;
int min_gid;
} and_config;
/* Initialise configuration parameters. Some are later over-
ridden by command-line arguments and and.conf. */
void set_defaults (int argc, char **argv)
{
and_config.test = 0;
and_config.verbose = 0;
and_config.to_stdout = 0;
and_config.program = argv[0];
and_config.lock_interval = false;
and_config.interval = DEFAULT_INTERVAL;
and_config.config_file = DEFAULT_CONFIG_FILE;
and_config.database_file = DEFAULT_DATABASE_FILE;
and_config.nice_default = DEFAULT_NICE;
and_config.min_uid = 0;
and_config.min_gid = 0;
gethostname(and_config.hostname,511);
and_config.hostname[511] = 0;
}
/* Log AND messages (to ./debug.and if in test mode, syslog() otherwise).
If available, uses non-ANSI function vsnprintf() to avoid possible
buffer overflow. */
void and_printf (int required_verbosity, char *fmt, ...)
{
va_list args;
static int syslog_open = 0;
static FILE *out = NULL;
char buffer [2048];
time_t t;
va_start(args,fmt);
if (and_config.verbose >= required_verbosity)
{
if (and_config.test) {
/* in test mode, create log file; use stderr on failure */
if (!and_config.to_stdout && !out) {
out = fopen("./debug.and","wt");
if (!out) {
out = stderr;
}
}
/* write time stamp to ./debug.and */
t = time(NULL);
strncpy(buffer,ctime(&t),2047);
buffer[strlen(buffer)-1] = ' ';
if (and_config.to_stdout)
fputs(buffer,stdout);
else
fputs(buffer,out);
}
/* build actual log message */
#ifdef HAVE_VSNPRINTF
vsnprintf(buffer,2048,fmt,args);
#else
vsprintf(buffer,fmt,args); /* ... and hope for the best :( */
buffer[2047] = 0;
#endif
if (and_config.to_stdout) {
fputs(buffer,stdout);
fflush(stdout);
} else {
if (and_config.test) {
/* log to ./debug.and in test mode */
fputs(buffer,out);
fflush(out);
} else {
/* write to syslog if in full operations */
if (!syslog_open) {
openlog(and_config.program,LOG_PERROR|LOG_PID,LOG_DAEMON);
syslog_open = 1;
}
syslog(LOG_WARNING,"%s",buffer);
}
}
}
va_end(args);
}
/* Print priority database. */
void print_priorities ()
{
int i;
and_printf(0,"Priority database:\n");
and_printf(0,"UID: GID: Command Parent: NLs:\n");
for (i=0; i<and_db.n; i++) {
and_printf(0,"%5i %5i %-20s %-20s %-13s %2i,%2i,%2i\n",
and_db.entry[i].uid, and_db.entry[i].gid,
and_db.entry[i].command_str,
and_db.entry[i].parent_str,
(and_db.entry[i].parentmode == PARENT_ONLY ? "(parent)" : "(ancestors)"),
and_db.entry[i].nl[0],
and_db.entry[i].nl[1], and_db.entry[i].nl[2]);
}
and_printf(0,"%i entries.\n\n",and_db.n);
}
/* Print configuration parameters. */
void print_config ()
{
and_printf(0,"Configuration parameters:\n"
"host name: %s\n"
"operational mode: %s\n"
"verbosity: %2i\n"
"default nicelevel: %2i\n"
"interval [sec]: %3u\n"
"level 0 from [sec]: %3u\n"
"level 1 from [sec]: %3u\n"
"level 2 from [sec]: %3u\n"
"minimum uid: %i\n"
"minimum gid: %i\n"
"affinity: %s\n"
" U: %i\n"
" G: %i\n"
" C: %i\n"
" P: %i\n\n",
and_config.hostname,
(and_config.test?"just checkin'":"I'm serious."),
and_config.verbose,
and_config.nice_default, and_config.interval,
and_config.time_mark[0], and_config.time_mark[1],
and_config.time_mark[2],
and_config.min_uid, and_config.min_gid,
and_config.affinity,
and_config.weight[PRI_U], and_config.weight[PRI_G],
and_config.weight[PRI_C], and_config.weight[PRI_P]);
}
/* Parse one line of the priority database */
void read_priorities ()
{
FILE *priority;
int bad_count, line_count;
char buffer [1024];
/* entry field */
int uid;
char uid_s [1024];
int gid;
char gid_s [1024];
char command [1024];
int parentmode;
char parent [1024];
char parent_s [1024];
int nl0, nl1, nl2;
/* auxillary structs */
struct passwd *lookup_p;
struct group *lookup_g;
int error;
char error_msg [1024];
int i, entry;
int linelen;
regex_t *rex;
bool section_matches = true;
if ((priority = fopen(and_config.database_file,"rt")) == 0) {
and_printf(0,"Priority database %s not found. Aborting.\n",
and_config.database_file);
abort();
}
and_printf(0,"Priority database is: %s\n", and_config.database_file);
/* Read file line by line */
line_count = bad_count = 0;
and_db.n = 0;
while (!feof(priority)) {
memset(buffer,0,1024);
if (fgets(buffer,1022,priority) == 0) break;
line_count++;
/* Intercept empty lines, comments, and overflows */
linelen = strlen(buffer);
if (linelen == 0) {
continue;
}
if (buffer[0] == 10 || buffer[0] == 13 || buffer[0] == '#') {
continue;
}
if (linelen > 1022) {
and_printf(0,"Priority database line %i too long: %s\n",
line_count, buffer);
bad_count++;
continue;
}
/* Handle host-specific parts */
if ((buffer[0] == 'o') && (buffer[1] == 'n') && (buffer[2] == ' ')) {
while (buffer[strlen(buffer)-1] < 32) {
buffer[strlen(buffer)-1] = 0;
}
rex = (regex_t*)malloc(sizeof(regex_t));
regcomp(rex,&buffer[3],REG_NOSUB|REG_EXTENDED);
section_matches = (regexec(rex,and_config.hostname,0,0,0) == 0);
and_printf(0,"Priority database line %i: section for host(s) %s will be %s.\n",
line_count, &buffer[3], (section_matches?"read":"skipped"));
regfree(rex);
continue;
}
if (!section_matches) {
continue;
}
i = sscanf(buffer, "%s %s %s %s %i %i %i",
uid_s, gid_s, command, parent_s, &nl0, &nl1, &nl2);
if (i != 7) {
and_printf(0,"Priority database line %i is invalid: %s\n",
line_count, buffer);
bad_count++;
continue;
}
/* Identify UID */
if (strcmp(uid_s,"*") == 0) {
uid = -1;
} else if ((lookup_p = getpwnam(uid_s)) != 0) {
uid = lookup_p->pw_uid;
} else if ((i = atoi(uid_s)) > 0) {
uid = i;
} else {
and_printf(0,"Priority database line %i with invalid UID: %s\n",
line_count, uid_s);
bad_count++;
continue;
}
/* Identify GID */
if (strcmp(gid_s,"*") == 0) {
gid = -1;
} else if ((lookup_g = getgrnam(gid_s)) != 0) {
gid = lookup_g->gr_gid;
} else if ((i = atoi(gid_s)) > 0) {
gid = i;
} else {
and_printf(0,"Priority database line %i with invalid GID: %s\n",
line_count, gid_s);
bad_count++;
continue;
}
/* figure parent mode */
if (strcmp(parent_s,"*") == 0) {
strcpy(parent,"*");
parentmode = PARENT_ONLY;
} else if (strncmp(parent_s,PARENT_ONLY_KEYWORD,
strlen(PARENT_ONLY_KEYWORD)) == 0) {
strcpy(parent,&parent_s[strlen(PARENT_ONLY_KEYWORD)]);
parentmode = PARENT_ONLY;
} else if (strncmp(parent_s,PARENT_AND_ANCHESTORS_KEYWORD,
strlen(PARENT_AND_ANCHESTORS_KEYWORD)) == 0) {
strcpy(parent,&parent_s[strlen(PARENT_AND_ANCHESTORS_KEYWORD)]);
parentmode = PARENT_AND_ANCHESTORS;
} else {
and_printf(0,"Priority database line %i with bad parent keyword: %s\n",
line_count, parent_s);
bad_count++;
continue;
}
/* Find entry in database */
entry = and_db.n;
for (i=0; i<and_db.n; ++i) {
if (and_db.entry[i].uid == uid && and_db.entry[i].gid == gid
&& strcmp(and_db.entry[i].command_str,command) == 0
&& strcmp(and_db.entry[i].parent_str,parent) == 0) {
entry = i;
break;
}
}
/* If not recycling an entry (i.e. overwriting the priorities),
rebuild the regular expression */
if (!and_db.entry[entry].command) {
and_db.entry[entry].uid = uid;
and_db.entry[entry].gid = gid;
and_db.entry[entry].command = (regex_t*)malloc(sizeof(regex_t));
error = regcomp(and_db.entry[entry].command,command,REG_NOSUB);
if (error) {
regerror(error,and_db.entry[entry].command,error_msg,1023);
regfree(and_db.entry[entry].command);
free(and_db.entry[entry].command);
and_db.entry[entry].command = NULL;
and_printf(0,"Priority database line %i with bad command regexp %s (%s)\n",
line_count, command, error_msg);
bad_count++;
continue;
}
strncpy(and_db.entry[entry].command_str,command,127);
and_db.entry[entry].command_str[127] = 0;
and_db.entry[entry].parent = (regex_t*)malloc(sizeof(regex_t));
error = regcomp(and_db.entry[entry].parent,parent,REG_NOSUB);
if (error) {
regerror(error,and_db.entry[entry].parent,error_msg,1023);
regfree(and_db.entry[entry].parent);
free(and_db.entry[entry].parent);
and_db.entry[entry].parent = NULL;
and_printf(0,"Priority database line %i with bad parent regexp %s (%s)\n",
line_count, parent, error_msg);
bad_count++;
continue;
}
strncpy(and_db.entry[entry].parent_str,parent,127);
and_db.entry[entry].parent_str[127] = 0;
and_db.entry[entry].parentmode = parentmode;
}
and_db.entry[entry].nl[0] = nl0;
and_db.entry[entry].nl[1] = nl1;
and_db.entry[entry].nl[2] = nl2;
if (entry == and_db.n) {
and_db.n++;
}
}
/* cleanup */
fclose(priority);
if (and_config.verbose > 1) {
print_priorities();
}
if (bad_count) {
and_printf(0,"Priority database contains %i bad lines. Aborting.\n",
bad_count);
abort();
}
}
void read_config ()
{
FILE *f;
int i, val, bad, bad_f, line, u, g, c, p;
unsigned uval;
char buffer [1024];
char param [1024];
char value [1024];
regex_t *rex;
bool section_matches = true;
if ((f = fopen(and_config.config_file,"rt")) == 0) {
and_printf(0,"Configuration file %s not found. Aborting.\n",
and_config.config_file);
abort();
}
and_printf(0,"Configuration file is: %s\n", and_config.config_file);
/* Set defaults */
strcpy(and_config.affinity,"ugcp");
and_config.weight[PRI_U] = 8;
and_config.weight[PRI_G] = 4;
and_config.weight[PRI_C] = 2;
and_config.weight[PRI_P] = 1;
and_config.time_mark[0] = 120; /* 2 min */
and_config.time_mark[1] = 1200; /* 20 min */
and_config.time_mark[2] = 3600; /* 60 min */
/* Read file line by line */
line = 0;
bad = 0;
fgets(buffer,1023,f);
while (!feof(f)) {
++line;
if (buffer[0] == 10 || buffer[0] == 13 || buffer[0] == '#') {
memset(buffer,0,1024);
if (fgets(buffer,1022,f) == 0) break;
continue;
}
if ((buffer[0] == 'o') && (buffer[1] == 'n') && (buffer[2] == ' ')) {
while (buffer[strlen(buffer)-1] < 32) {
buffer[strlen(buffer)-1] = 0;
}
rex = (regex_t*)malloc(sizeof(regex_t));
regcomp(rex,&buffer[3],REG_NOSUB|REG_EXTENDED);
section_matches = (regexec(rex,and_config.hostname,0,0,0) == 0);
and_printf(0,"Configuration file line %i: section for host(s) %s will be %s.\n",
line, &buffer[3], (section_matches?"read":"skipped"));
regfree(rex);
buffer[0] = '#'; /* trigger read next line */
continue;
}
if (!section_matches) {
buffer[0] = '#'; /* trigger read next line */
continue;
}
if (sscanf(buffer,"%s %s",param,value) != 2) {
++bad;
and_printf(0,"Configuration file line %i is invalid: %s\n",
line, buffer);
memset(buffer,0,1024);
if (fgets(buffer,1022,f) == 0) break;
continue;
}
if (sscanf(value,"%i",&val) != 1) val = -1;
if (sscanf(value,"%u",&uval) != 1) uval = UINT_MAX;
if (strcmp(param,"defaultnice")==0) {
if (val > -1) /* Prohibits a default is kill policy */
and_config.nice_default = val;
else {
++bad;
and_printf(0,"Configuration file line %i has invalid value for defaultnice: %s.\n",
line, value);
}
} else if (strcmp(param,"interval")==0) {
if (and_config.lock_interval) {
and_printf(0,"Configuration file line %i: interval locked by -i command-line option.\n",
line);
} else {
if (uval < UINT_MAX)
and_config.interval = uval;
else {
++bad;
and_printf(0,"Configuration file line %i has invalid value for interval: %s.\n",
line, value);
}
}
} else if (strcmp(param,"minuid")==0) {
if (uval < UINT_MAX)
and_config.min_uid = uval;
else {
++bad;
and_printf(0,"Configuration file line %i has invalid value for minuid: %s.\n",
line, value);
}
} else if (strcmp(param,"mingid")==0) {
if (uval < UINT_MAX)
and_config.min_gid = uval;
else {
++bad;
and_printf(0,"Configuration file line %i has invalid value for mingid: %s.\n",
line, value);
}
} else if (strcmp(param,"lv1time")==0) {
if (uval < UINT_MAX)
and_config.time_mark[0] = uval;
else {
++bad;
and_printf(0,"Configuration file line %i has invalid value for lv1time: %s.\n",
line, value);
}
} else if (strcmp(param,"lv2time")==0) {
if (uval < UINT_MAX)
and_config.time_mark[1] = uval;
else {
++bad;
and_printf(0,"Configuration file line %i has invalid value for lv2time: %s.\n",
line, value);
}
} else if (strcmp(param,"lv3time")==0) {
if (uval < UINT_MAX)
and_config.time_mark[2] = uval;
else {
++bad;
and_printf(0,"Configuration file line %i has invalid value for lv3time: %s.\n",
line, value);
}
} else if (strcmp(param,"affinity")==0) {
bad_f = -1;
u = g = c = p = 0;
if (strlen(value) != 4) {
and_printf(0,"Configuration file line %i has invalid affinity: %s.\n",
line, value);
++bad;
} else {
for (i=0; i<4; i++) {
switch(value[3-i]) {
case 'u':
if (!u) u = and_config.weight[PRI_U] = 1 << i;
else bad_f = i;
break;
case 'g':
if (!g) g = and_config.weight[PRI_G] = 1 << i;
else bad_f = i;
break;
case 'c':
if (!c) c = and_config.weight[PRI_C] = 1 << i;
else bad_f = i;
break;
case 'p':
if (!p) p = and_config.weight[PRI_P] = 1 << i;
else bad_f = i;
break;
default:
bad_f = 3-i;
}
}
if (bad_f > -1) {
and_printf(0,"Configuration file line %i has invalid affinity: %s[%i]=%c.\n",
line, value, bad_f, value[bad_f]);
++bad;
} else {
strncpy(and_config.affinity,value,4);
and_config.affinity[4] = 0;
}
}
} else {
++bad;
and_printf(0,"Configuration file line %i has invalid parameter: %s.\n",
line, param);
}
memset(buffer,0,1024);
if (fgets(buffer,1022,f) == 0) break;
}
/* Cleanup, exit on errors. */
fclose(f);
if (and_config.verbose > 1) print_config();
if (bad) {
and_printf(0,"Configuration file contains %i bad lines. Aborting.\n",
bad);
abort();
}
}
/* Compute new nice level for given command/uid/gid/utime */
int and_getnice (int uid, int gid, char *command, struct and_procent *parent, unsigned cpu_seconds)
{
int i, level, entry, exact = -1, last;
struct and_procent *par;
int exactness [PRI_MAXENTRIES];
if (!command) {
and_printf(0,"Process without command string encountered. Aborting.\n");
abort();
}
if (uid == 0) {
and_printf(3,"root is untouchable: %s\n", command);
return 0;
}
if (uid < and_config.min_uid) {
and_printf(3,"uid %i is untouchable: %s\n", uid, command);
return 0;
}
if (gid < and_config.min_gid) {
and_printf(3,"gid %i is untouchable: %s\n", gid, command);
return 0;
}
/* Strategy: each priority database accumulates accuracy points
for every aspect: user, group, command, parent. An exact hit is
worth the configured weight of the aspect (1, 2, 4, 8); a joker
is worth 0; and a miss is with -MAXINT, effectively eliminating
the entry (veto). At the end, the highest rated entry is
used to determine the new nice level. */
for (i=0; i<and_db.n; i++) {
/* user id */
if (uid == and_db.entry[i].uid) {
exactness[i] = and_config.weight[PRI_U];
} else if (and_db.entry[i].uid == -1) {
exactness[i] = 0;
} else {
exactness[i] = -INT_MAX;
}
/* group id */
if (gid == and_db.entry[i].gid) {
exactness[i] += and_config.weight[PRI_G];
} else if (and_db.entry[i].gid == -1) {
exactness[i] += 0;
} else {
exactness[i] = -INT_MAX;
}
/* command */
if (command!=NULL && regexec(and_db.entry[i].command,command,0,0,0) == 0) {
exactness[i] += and_config.weight[PRI_C];
} else if (strcmp(and_db.entry[i].command_str,"*") == 0) {
exactness[i] += 0;
} else {
exactness[i] = -INT_MAX;
}
/* parent */
par = parent;
while (par != NULL)
{
last = (and_db.entry[i].parentmode == PARENT_ONLY ||
par->parent == NULL);
if (regexec(and_db.entry[i].parent,par->command,0,0,0) == 0) {
exactness[i] += and_config.weight[PRI_P];
break;
} else if (last && strcmp(and_db.entry[i].parent_str,"*") == 0) {
exactness[i] += 0;
break;
} else if (last) {
exactness[i] = -INT_MAX;
break;
}
par = par->parent;
}
}
entry = 0;
exact = -1;
for (i=0; i<and_db.n; i++) {
if (exactness[i] >= exact) {
/* >exact -> first entry wins, >=exact -> last entry wins */
entry = i;
exact = exactness[i];
}
}
if (exact < 0) {
and_printf(2,"no match for uid=%i gid=%i cmd=%s\n par=%s\n",
uid, gid, command, (parent!=NULL?parent->command:"(orphan)"));
return and_config.nice_default;
}
level = 2;
while (level >= 0 && and_config.time_mark[level] > cpu_seconds) {
--level;
}
and_printf(2,"command=%s (%i,%i,%s) hit on entry=%i, exactness=%i, level=%i.\n",
command, uid, gid, (parent!=NULL?parent->command:"(orphan)"),
entry, exact, level);
return (level >= 0 ? and_db.entry[entry].nl[level] : 0);
}
/**********************************************************************
**********************************************************************/
static struct and_procent *(*and_getfirst)() = NULL;
static struct and_procent *(*and_getnext)() = NULL;
void and_setprocreader (struct and_procent *(*getfirst)(),
struct and_procent *(*getnext)())
{
and_getfirst = getfirst;
and_getnext = getnext;
}
struct and_procent* and_find_proc (struct and_procent *head, int ppid)
{
struct and_procent *current = head;
while (current != NULL)
{
if (current->pid == ppid)
return current;
current = current->next;
}
and_printf(1,"no parent for ppid: %d\n", ppid);
return NULL;
}
void and_loop ()
{
struct and_procent *head, *current, *new, *proc;
int newnice;
int njobs = 0;
assert(and_getfirst != NULL);
assert(and_getnext != NULL);
head = NULL;
current = NULL;
proc = and_getfirst();
while (proc != NULL) {
new = (struct and_procent*)malloc(sizeof(struct and_procent));
memcpy(new,proc,sizeof(struct and_procent));
new->next = NULL;
if (current != NULL) {
current->next = new;
} else {
head = new;
}
current = new;
proc = and_getnext();
}
current = head;
while (current != NULL) {
if (current->pid != current->ppid)
current->parent = and_find_proc(head,current->ppid);
else
current->parent = NULL;
and_printf(2, "process %s parent : %s\n", current->command,
(current->parent != NULL ? current->parent->command : "(none)"));
current = current->next;
}
current = head;
while (current != NULL) {
njobs++;
newnice = and_getnice(current->uid,current->gid,current->command,
current->parent,current->utime);
if (current->uid != 0) {
if (newnice) {
if (newnice > 0) {
if (newnice > current->nice) {
if (and_config.test)
and_printf(0,"would renice to %i: %i (%s)\n",newnice,current->pid,
current->command);
else {
and_printf(1,"renice to %i: %i (%s)\n",newnice,current->pid,
current->command);
setpriority(PRIO_PROCESS,current->pid,newnice);
}
}
} else {
if (and_config.test)
and_printf(0,"would kill %i %i (%s)\n",newnice,current->pid,
current->command);
else {
and_printf(1,"kill %i %i (%s)\n",newnice,current->pid,
current->command);
kill(current->pid,-newnice);
}
}
}
}
current = current->next;
}
current = head;
while (current != NULL) {
proc = current;
current = current->next;
free(proc);
}
}
void and_getopt (int argc, char** argv)
{
#define OPTIONS "c:d:i:vstxh"
int opt, value;
opt = getopt(argc,argv,OPTIONS);
while (opt != -1) {
switch(opt) {
case 'c':
and_config.config_file = (char*)malloc(strlen(optarg)+1);
assert(and_config.config_file);
strcpy(and_config.config_file,optarg);
break;
case 'd':
and_config.database_file = (char*)malloc(strlen(optarg)+1);
assert(and_config.database_file);
strcpy(and_config.database_file,optarg);
break;
case 'i':
value = atoi(optarg);
if (value > 0) {
and_config.lock_interval = true;
and_config.interval = value;
} else {
fprintf(stderr,"%s: illegal interval: %s\n",argv[0],optarg);
exit(1);
}
break;
case 's':
and_config.to_stdout = 1;
break;
case 'v':
++and_config.verbose;
break;
case 't':
and_config.test = 1;
break;
case 'x':
and_config.test = 0;
break;
case 'h':
printf("auto nice daemon version %s (%s)\n"
"%s [-v] [-s] [-t] [-x] [-c configfile] [-d databasefile] [-i interval]\n"
"-v: verbosity -v, -vv, -vvv etc\n"
"-s: log to stdout (default is syslog, or debug.and)\n"
"-x: really execute renices and kills (default)\n"
"-t: test configuration (don't really renice)\n"
"-i interval: loop interval in seconds (default %i)\n"
"-c configfile: specify config file (default %s)\n"
"-d databasefile: specify priority database file (default %s)\n"
,AND_VERSION,AND_DATE,argv[0],
DEFAULT_INTERVAL, DEFAULT_CONFIG_FILE, DEFAULT_DATABASE_FILE);
exit(1);
default:
fprintf(stderr,"Try %s -h for help.\n", argv[0]);
exit(1);
}
opt = getopt(argc,argv,OPTIONS);
}
#undef OPTIONS
}
static int g_reload_conf;
void and_trigger_readconf (int sig)
{
g_reload_conf = (sig == SIGHUP);
}
void and_readconf ()
{
and_printf(0,"Re-reading configuration and priority database...\n");
read_config();
read_priorities();
g_reload_conf = 0;
}
void and_worker ()
{
read_config();
read_priorities();
signal(SIGHUP,and_trigger_readconf);
and_printf(0,"AND ready.\n");
g_reload_conf = 0;
while (1) {
if (g_reload_conf) {
and_readconf();
}
and_loop();
sleep(and_config.interval);
}
}
int and_main (int argc, char** argv)
{
set_defaults(argc,argv);
and_getopt(argc,argv);
if (and_config.test) {
and_worker();
} else {
if (fork() == 0) and_worker();
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1