isdn4k-utils/vbox/src/vboxbeep.c

637 lines
14 KiB
C

/*
** $Id: vboxbeep.c,v 1.9 2005/02/23 16:26:57 keil Exp $
**
** Copyright (C) 1996, 1997 Michael 'Ghandi' Herold
*/
#include "config.h"
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <paths.h>
#include <string.h>
#include <dirent.h>
#include <syslog.h>
#include <netdb.h>
#include <netinet/in.h>
#include "vboxbeep.h"
#include "libvbox.h"
/** Variables *************************************************************/
static struct messagebox messageboxes[MAX_MESSAGE_BOXES];
static char sound_available_hours[24];
static char *vbasename = NULL;
static char *pidname = NULL;
static int killmode = 0;
static time_t starttime = 0;
static int dodebug = FALSE;
static struct option arguments[] =
{
{ "version" , no_argument , NULL, 'v' },
{ "help" , no_argument , NULL, 'h' },
{ "kill" , no_argument , NULL, 'k' },
{ "sound" , required_argument , NULL, 's' },
{ "messagebox" , required_argument , NULL, 'm' },
{ "pause" , required_argument , NULL, 'p' },
{ "debug" , no_argument , NULL, 'x' },
{ NULL , 0 , NULL, 0 }
};
/** Prototypes ************************************************************/
static void lets_hear_the_sound(void);
static int add_dir_to_messagebox(int, char *);
static void free_all_messageboxes(void);
static time_t get_newest_message_time(char *);
static void beep(int);
static int get_pid_from_file(void);
static void remove_pid_file(void);
static void create_pid_file(void);
static void leave_program(int);
static void set_new_starttime(int);
static void free_resources(void);
static void init_signals();
static void log(int, char *, ...);
static void parse_sound_times(char *, int, int);
static void version(void);
static void usage(void);
/**************************************************************************/
/** The magic main... **/
/**************************************************************************/
int main(int argc, char **argv)
{
char timestrings[32];
int i;
int b;
int opts;
int checkpause;
if (!(vbasename = rindex(argv[0], '/')))
{
vbasename = argv[0];
}
else vbasename++;
for (i = 0; i < MAX_MESSAGE_BOXES; i++)
{
messageboxes[i].name = NULL;
messageboxes[i].time = 0;
}
parse_sound_times(NULL, 0, 23);
if (!(pidname = malloc(strlen(PIDFILEDIR) + strlen("/vboxbeep.pid") + 1)))
{
log(LOG_ERR, "not enough memory to create pid name.\n", vbasename);
exit(5);
}
printstring(pidname, "%s/vboxbeep.pid", PIDFILEDIR);
killmode = 0;
starttime = 0;
checkpause = 5;
b = 0;
while ((opts = getopt_long(argc, argv, "vhks:m:p:x", arguments, (int *)0)) != EOF)
{
switch (opts)
{
case 'x':
dodebug = TRUE;
break;
case 'k':
killmode = 1;
break;
case 'p':
checkpause = (int)xstrtol(optarg, 5);
break;
case 'm':
if (add_dir_to_messagebox(b, optarg)) b++;
break;
case 's':
parse_sound_times(strdup(optarg), 0, 23);
break;
case 'v':
version();
break;
case 'h':
default:
usage();
break;
}
}
init_signals();
i = get_pid_from_file();
if (i)
{
if (killmode == 0)
{
fprintf(stderr, "%s: sending signal to stop beep's to process %d...\n", vbasename, i);
if (kill(i, SIGUSR1) != 0)
{
fprintf(stderr, "%s: error sending signal (%s).\n", vbasename, strerror(errno));
}
}
else
{
if (geteuid() == 0)
{
fprintf(stderr, "%s: sending terminate signal to process %d...\n", vbasename, i);
if (kill(i, SIGTERM) != 0)
{
fprintf(stderr, "%s: error sending signal (%s).\n", vbasename, strerror(errno));
}
}
else
{
fprintf(stderr, "%s: you must be *root* to kill a running %s.\n", vbasename, vbasename);
}
}
free_resources();
exit(1);
}
if (killmode == 1)
{
fprintf(stderr, "%s: can't find running %s process.\n", vbasename, vbasename);
free_resources();
exit(5);
}
if (geteuid() != 0)
{
fprintf(stderr, "%s: you must be *root* to start %s.\n", vbasename, vbasename);
free_resources();
exit(5);
}
if (!b)
{
log(LOG_CRIT, "there are no directories to watch.");
free_resources();
exit(5);
}
create_pid_file();
for (i = 0, *timestrings = '\0'; i < 24; i++)
{
strcat(timestrings, sound_available_hours[i] ? "x" : "-");
}
if (dodebug)
{
log(LOG_DEBUG, "time range \"%s\".", timestrings);
}
set_new_starttime(0);
while (TRUE)
{
for (i = 0; i < MAX_MESSAGE_BOXES; i++)
{
if (messageboxes[i].name)
{
messageboxes[i].time = get_newest_message_time(messageboxes[i].name);
if (messageboxes[i].time > starttime)
{
lets_hear_the_sound();
break;
}
}
}
xpause(checkpause * 1000);
}
leave_program(0);
}
/**************************************************************************/
/** version(): Displays package version. **/
/**************************************************************************/
static void version(void)
{
fprintf(stderr, "\n");
fprintf(stderr, "%s version %s (%s)\n", vbasename, VERSION, VERDATE);
fprintf(stderr, "\n");
free_resources();
exit(1);
}
/**************************************************************************/
/** usage(): Displays usage message. **/
/**************************************************************************/
static void usage(void)
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s OPTION [ OPTION ] [ ... ]\n", vbasename);
fprintf(stderr, "\n");
fprintf(stderr, "-s, --sound HOURS Hours to signal with sound (default: no time)\n");
fprintf(stderr, "-m, --messagebox DIR Watch directory DIR for new messages.\n");
fprintf(stderr, "-p, --pause SECONDS Pause in seconds to sleep between checks (default: 5).\n");
fprintf(stderr, "-h, --help Displays this help text.\n");
fprintf(stderr, "-v, --version Displays program version.\n");
fprintf(stderr, "\n");
fprintf(stderr, "Hours to play sound with the pc speaker must be specified in 24-hour-format.\nyou can use ',' or '-' to separate hours or specify time ranges (eg 9,17-22).\nA '--sound=\"*\"' means at every time and '--sound=\"-\"' means at no time.\n\nThe option '--messagebox' can be used more than one time, so you can watch\nmax. %d separate directories.\n", MAX_MESSAGE_BOXES);
fprintf(stderr, "\n");
free_resources();
exit(1);
}
/**************************************************************************/
/** init_signals(): Initialize the signals. **/
/**************************************************************************/
static void init_signals(void)
{
signal(SIGTERM , leave_program);
signal(SIGHUP , leave_program);
signal(SIGUSR1 , set_new_starttime);
signal(SIGQUIT , SIG_IGN);
signal(SIGINT , leave_program);
}
/**************************************************************************/
/** Free_Resources(): Frees all used resources. **/
/**************************************************************************/
static void free_resources(void)
{
if (pidname) free(pidname);
pidname = NULL;
free_all_messageboxes();
}
/**************************************************************************/
/** leave_program(): Leave the program. **/
/**************************************************************************/
static void leave_program(int sig)
{
int vt;
if ((vt = open("/dev/console", O_RDONLY)) != -1)
{
ioctl(vt, KIOCSOUND, 0);
close(vt);
}
remove_pid_file();
free_resources();
if (dodebug) log(LOG_DEBUG, "terminate...");
exit(0);
}
/**************************************************************************/
/** set_new_starttime(): Sets new watch start time. **/
/**************************************************************************/
static void set_new_starttime(int sig)
{
init_signals();
starttime = time(NULL);
if (dodebug) log(LOG_DEBUG, "new starttime is %ld.", starttime);
}
/**************************************************************************/
/** lets_hear_the_sound(): Plays one sound sequence. **/
/**************************************************************************/
static void lets_hear_the_sound(void)
{
struct tm *timel;
time_t timec;
int vt;
timec = time(NULL);
if (!(timel = localtime(&timec)))
{
log(LOG_CRIT, "can't get current local time (%s).", strerror(errno));
return;
}
if ((timel->tm_hour < 0) || (timel->tm_hour > 23))
{
log(LOG_CRIT, "bad hour in local time structure (%d).", timel->tm_hour);
return;
}
if (sound_available_hours[timel->tm_hour] == TRUE)
{
if ((vt = open("/dev/console", O_RDWR)) == -1)
{
log(LOG_CRIT, "can't open \"/dev/console\" (%s).", strerror(errno));
return;
}
beep(vt);
close(vt);
}
}
/**************************************************************************/
/** beep(): Plays one beep. **/
/**************************************************************************/
static void beep(int vt)
{
ioctl(vt, KIOCSOUND, 3200);
xpause(50);
ioctl(vt, KIOCSOUND, 5200);
xpause(20);
ioctl(vt, KIOCSOUND, 0);
xpause(25);
}
/**************************************************************************/
/** add_dir_to_messagebox(): Adds a directory to the watchlist. **/
/**************************************************************************/
static int add_dir_to_messagebox(int nr, char *box)
{
char *name;
if (nr < MAX_MESSAGE_BOXES)
{
if ((name = strdup(box)))
{
messageboxes[nr].name = name;
messageboxes[nr].time = time(NULL);
returnok();
}
}
log(LOG_ERR, "can't add \"%s\" to watchlist.", box);
returnerror();
}
/**************************************************************************/
/** free_all_messageboxes(): Frees messagebox resources. **/
/**************************************************************************/
static void free_all_messageboxes(void)
{
int i;
for (i = 0; i < MAX_MESSAGE_BOXES; i++)
{
if (messageboxes[i].name) free(messageboxes[i].name);
messageboxes[i].name = NULL;
}
}
/**************************************************************************/
/** get_newest_message_time(): Scans a messagebox and returns the time **/
/** of the newest message. **/
/**************************************************************************/
static time_t get_newest_message_time(char *box)
{
struct stat status;
struct dirent *one;
time_t newest;
DIR *dir;
newest = 0;
if (chdir(box) == 0)
{
if ((dir = opendir(".")))
{
while ((one = readdir(dir)))
{
/* ignore filenames starting with '.' */
if ((one->d_name[0] != '.')
&& (stat(one->d_name, &status) == 0)
&& S_ISREG(status.st_mode))
{
if (status.st_mtime > newest)
newest = status.st_mtime;
}
}
closedir(dir);
}
else log(LOG_ERR, "can't open '%s'.", box);
}
else log(LOG_ERR, "can't change to '%s'.", box);
return(newest);
}
/**************************************************************************/
/** create_pid_file(): Creates the program pid lock. **/
/**************************************************************************/
static void create_pid_file(void)
{
FILE *pid;
if (!(pid = fopen(pidname, "w")))
{
log(LOG_CRIT, "could not create PID file \"%s\" (%s).", pidname, strerror(errno));
leave_program(0);
}
fprintf(pid, "%d\n", getpid());
fclose(pid);
}
/**************************************************************************/
/** remove_pid_file(): Remove the programs PID lock. **/
/**************************************************************************/
static void remove_pid_file(void)
{
if (unlink(pidname) != 0)
{
log(LOG_CRIT, "can't remove PID file \"%s\" (%s).", pidname, strerror(errno));
}
}
/**************************************************************************/
/** get_pid_from_file(): Returns the PID from the PID file. **/
/**************************************************************************/
static int get_pid_from_file(void)
{
char temp[32];
FILE *pid;
if ((pid = fopen(pidname, "r")))
{
fgets(temp, 32, pid);
fclose(pid);
return(atoi(temp));
}
return(0);
}
/**************************************************************************/
/** parse_sound_times(): Parse the string for time zones. **/
/**************************************************************************/
static void parse_sound_times(char *string, int min, int max)
{
char *number;
char *beg;
char *end;
int begnr;
int endnr;
int i;
if ((!string) || (strcmp(string, "!") == 0) || (strcmp(string, "-") == 0))
{
for (i = min; i <= max; i++)
{
sound_available_hours[i] = FALSE;
}
return;
}
if (strcmp(string, "*") == 0)
{
free(string);
for (i = min; i <= max; i++)
{
sound_available_hours[i] = TRUE;
}
return;
}
number = strtok(string, ",;:");
while (number)
{
beg = number;
if ((end = index(number, '-')))
{
*end++ = '\0';
}
else end = beg;
begnr = atoi(beg);
endnr = atoi(end);
if ((begnr >= min) && (begnr <= max) && (endnr >= min) && (endnr <= max))
{
if (endnr >= begnr)
{
for (i = begnr; i <= endnr; i++)
{
sound_available_hours[i] = TRUE;
}
}
else log(LOG_ERR, "starttime must be lower than endtime.");
}
else log(LOG_ERR, "start- or endtime out of range.");
number = strtok(NULL, ",;:");
}
free(string);
}
/**************************************************************************/
/** log(): Writes a message to the syslog. **/
/**************************************************************************/
static void log(int level, char *fmt, ...)
{
va_list arg;
openlog(vbasename, LOG_CONS|LOG_PID, LOG_USER);
va_start(arg, fmt);
vsyslog(level, fmt, arg);
va_end(arg);
closelog();
}