/*
* Oupsd: monitoring des lignes dcd et dsr sur port serie
* pour simuler un onduleur et arreter une machine
* sous linux, ayant des problemes de clavier et
* aucune connection reseau pour se connecter.
* Usage: oupsd /dev/ttyS0 (or any other serial device) number.
*
* Author: R. Suinot, < rsuinux@gmx.fr >.
*
* Tres largement inspire de:
* powerd de Miquel van Smoorenburg, < miquels@drinkel.cistron.nl >
* en sa version 1.31, du 29-Feb-1996
* et de:
* setserialbits.c du Alessandro Rubini, rubini@ipvvis.unipv.it.
*
* Version: 1.0 29 Avril 2002
*
*
* 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 Library 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.
*/
/* Use the new way of communicating with init. */
#define NEWINIT
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <syslog.h>
#include <string.h>
#include <ctype.h>
#include "paths.h"
#ifdef NEWINIT
#include "initreq.h"
#endif
#include <linux/kd.h>
#ifndef SIGPWR
# define SIGPWR SIGUSR1
#endif
#ifdef NEWINIT
void alrm_handler( void );
void alrm_handler()
{
}
#endif
/* Define personnel pour les signaux recus */
#define _OUPS_INIT 16390
#define _OUPS_DSR 16646
#define _OUPS_DCD 16454
/* La definition de PWRSTAT est obligatoire (definition du fichier de status nécésaire à init) */
#define PWRSTAT "/etc/powerstatus"
#ifndef CLOCK_TICK_RATE
#define CLOCK_TICK_RATE 1193180
#endif
/* Meaningful Defaults */
#define DEFAULT_FREQ 1000
#define DEFAULT_LENGTH 50
#define DEFAULT_REPS 1
#define DEFAULT_DELAY 100 /* milliseconds */
/*
j'utilise une partie du code de beep-current (version beep-1.0.2)
pour envoyer un beep sur le buzer
*/
typedef struct
{
int freq; /* tone frequency (Hz) */
int length; /* tone length (ms) */
int reps; /* # of repetitions */
int delay; /* delay between reps (ms) */
}
beep_parms_t;
/* ------------------------------------------------------------------------------------------------ */
/* Déclarations des fonctions */
void powerfail (int ok);
void play_beep (beep_parms_t parms);
/* ------------------------------------------------------------------------------------------------ */
/* Tell init the power has either gone or is back. */
void powerfail (ok)
int ok;
{
int fd;
/* Creation d'un fichier d'initialisation pour init */
unlink (PWRSTAT);
if ((fd = open (PWRSTAT, O_CREAT | O_WRONLY, 0644)) >= 0)
{
if (ok == 0)
write (fd, "FAIL\n", 5);
else if (ok == 1)
write (fd, "OK\n", 5);
else
write (fd, "LOW\n", 5);
close (fd);
}
else
{
syslog (LOG_INFO, "Could not open or create /etc/powerfail for writing");
}
kill(1, SIGPWR);
}
/* fonction beeper */
void play_beep (beep_parms_t parms)
{
int console_fd = -1;
int i; /* loop counter */
/* try to snag the console */
if ((console_fd = open ("/dev/console", O_WRONLY)) == -1)
{
syslog (LOG_INFO, "Could not open /dev/console for writing");
exit (1);
}
/* Beep */
for (i = 0; i < parms.reps; i++)
{ /* start beep */
if (ioctl (console_fd, KIOCSOUND, CLOCK_TICK_RATE / parms.freq) < 0)
{
syslog (LOG_INFO, "error with ioctl.");
}
usleep (1000 * parms.length); /* wait... */
ioctl (console_fd, KIOCSOUND, 0); /* stop beep */
usleep (1000 * parms.delay); /* wait... */
} /* repeat. */
close (console_fd);
}
/* Main program. */
int main (int argc, char **argv)
{
char *me = argv[0];
int fd;
int flags, timeout = 2;
int oldstatus = 0;
int count = 0, etat = 0;
uint mask = ~0, outb = 0;
beep_parms_t parms = { DEFAULT_FREQ, DEFAULT_LENGTH, DEFAULT_REPS, DEFAULT_DELAY };
if (argc != 2 || !strcmp (argv[1], "--help") || !strcmp (argv[1], "-h"))
{
printf (" Usage: \"%s <device> a\"\n", me);
printf (" <device> is a serial device, like /dev/ttyS0 or /dev/oups\n"
" a number report data every that many seconds\n\n");
exit (1);
}
/* Demarrage de syslog. */
openlog ("oupsd", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON);
/* si on ne peut pas ouvrir le periferique de sortie, on quitte */
if ((fd = open (argv[1], O_RDWR | O_NDELAY)) < 0)
{
syslog (LOG_ERR, "%s: %s: %s\n", me, argv[1], sys_errlist[errno]);
closelog ();
exit (1);
}
/* on traite le second parametre passe au demarrage */
if (isdigit (**argv))
timeout = atoi (*argv);
/* On passe en mode 'demon' */
switch(fork())
{
case 0: /* je suis le fils */
syslog(LOG_INFO, "Demarrage du demon oupsd: ok.");
closelog();
setsid();
break;
case -1: /* Error: passage en demon rate! */
syslog(LOG_ERR, "can't fork.");
closelog();
exit(1);
default: /* je suis le pere */
closelog();
exit(0);
}
/* Restart syslog. */
openlog("oupsd", LOG_CONS|LOG_PID, LOG_DAEMON);
/* Modif perso */
mask &= ~TIOCM_DTR;
outb |= TIOCM_DTR;
/* On ouvre la ligne avec les parametres de depart. Force it anyway to be sure. */
ioctl (fd, TIOCMGET, &flags);
flags &= mask;
flags |= outb;
ioctl (fd, TIOCMSET, &flags);
/* Demande le status actuel */
ioctl (fd, TIOCMGET, &flags);
/* Calcul le status actuel, et le garde de coté pour les testes de differences. */
oldstatus = flags;
switch ( flags )
{
case _OUPS_INIT:
etat++;
syslog (LOG_INFO, "Status au demarrage: ok" );
break;
case _OUPS_DSR:
case _OUPS_DCD:
syslog (LOG_INFO, "Le peripherique n'est pas dans l'etat attendu au demarrage.");
break;
}
/* a present, on scrute les lignes DSR et DCD, on n'agit que lorsqu'il y aura
* une difference entre status et oldstatus */
while (1)
{
/* Get the status. */
ioctl (fd, TIOCMGET, &flags);
if (flags != oldstatus)
{ /* changement de valeur de status */
if (flags == _OUPS_INIT)
{ /* le bouton est passé à l'état de départ, parce qu'on n'y était pas encore */
if (etat == 0)
{ /* c'est une mise à l'état de départ */
oldstatus = flags;
etat++;
count = 0;
play_beep (parms);
syslog (LOG_INFO, "Remise à zéro du bouton.");
}
}
else
{ /* on est passé soit à _OUPS_DCD soit à _OUPS_DSR */
play_beep (parms);
parms.reps++;
if (flags == _OUPS_DSR)
{ /* demande d'arret de la machine */
syslog (LOG_INFO, "modification sur DSR (%i) ", count);
}
else if (flags == _OUPS_DCD)
{ /* demande d'arret du serveur X */
syslog (LOG_INFO, "modification sur DCD (%i) ", count);
}
if (count++ > 2)
{ /* Si cela fait 3 fois que le status est changé, alors ce n'est pas une erreur,
* on envoi un mesage a init en fonction du message */
parms.freq += 5000;
parms.reps += 2;
play_beep (parms);
if (flags == _OUPS_DSR)
{ /* demande d'arret de la machine */
syslog (LOG_INFO, "Signal envoyé vers le processus init: ARRET SYSTEME ");
powerfail (2);
}
else if (flags == _OUPS_DCD)
{ /* demande d'arret du serveur X */
syslog (LOG_INFO, "Signal envoyé vers le processus init: ARRET PARTIEL ");
powerfail (0);
}
oldstatus = flags; /* on change la valeur de oldstatus, et on simule un demarrage avec le */
etat--; /* bouton mal positionne */
count = 0;
parms.reps = 1;
parms.freq = DEFAULT_FREQ;
}
}
}
else if (flags == _OUPS_INIT)
{ /* on essai d'annuler le changement demandé */
if (count != 0)
{ /* on s'est trompe, apparemment? donc on demande la restauration du processus init */
syslog (LOG_INFO, "Annulation de la demande d'arret du systeme.");
oldstatus = flags;
count = 0;
parms.reps = 1;
parms.freq = 3000;
play_beep (parms);
parms.freq = DEFAULT_FREQ;
}
}
sleep (timeout);
}
return (0);
}