/* empty - run processes under pseudo-terminal sessions
*
* Copyright (C) 2005-2007 Mikhail E. Zakharov
* empty was written by Mikhail E. Zakharov. This software was based on the
* basic idea of pty version 4.0 Copyright (c) 1992, Daniel J. Bernstein, but
* no code was ported from pty4.
*
* 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 immediately at the beginning of the file, without modification,
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR 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.
*/
/* AIX and OSF1 code by Sylvain DEGUT (sylvain.degut@neuf.fr) 12/2006 */
#ifdef __SCO_VERSION__ /* We want SCO to look like the SVR4 system */
#define __SVR4
#endif
#include <unistd.h>
#include <sys/types.h>
#if defined(__SVR4) || defined(__hpux__)
#include <stropts.h>
#endif
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <termios.h>
#ifdef __FreeBSD__
#include <libutil.h>
#endif
#ifdef __OpenBSD__
#include <util.h>
#define EIDRM EINVAL /* Thanks "Elisender" <allex_9@ngs.ru> */
#endif
#if defined (__linux__) || defined (__CYGWIN__)
#include <pty.h>
#include <utmp.h>
#include <time.h>
#include <sys/time.h>
#endif
#ifdef __AIX
#include <time.h>
#include <sys/stropts.h>
#include <sys/pty.h>
#include <utmp.h>
#endif
#ifdef __OSF1
#include <sys/termios.h>
#include <varargs.h>
#endif
#ifndef __hpux__
#include <sys/select.h>
#endif
#if !defined(__SVR4) && !defined(__hpux__) && !defined(__AIX) && !defined(__OSF1)
#include <err.h>
#endif
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <syslog.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/param.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <regex.h>
#define tmpdir "/tmp"
#define program "empty"
#define version "0.6.12b"
/* -------------------------------------------------------------------------- */
static void usage(void);
long toint(char *intstr);
void wait4child(int child, char *argv0);
int mfifo(char *fname, int mode);
void perrx(int ex_code, const char *err_text, ...);
void perrxslog(int ex_code, const char *err_text, ...);
long pidbyppid(pid_t ppid, int lflg);
void clean(void);
void fsignal(int sig);
int longargv(int argc, char *argv[]);
int checkgr(int argc, char *argv[], char *buf, int chkonly);
int regmatch(const char *string, char *pattern, regex_t *re);
int watch4str(int ifd, int ofd, int argc, char *argv[],
int Sflg, int vflg, int cflg, int timeout);
int parsestr(char *dst, char *src, int len, int Sflg);
/* -------------------------------------------------------------------------- */
int master, slave;
int child;
long pid = -1;
char *in = NULL, *out = NULL, *sl = NULL, *pfile = NULL;
int ifd, ofd, lfd = 0, pfd = 0;
FILE *pf;
int status;
char buf[BUFSIZ];
fd_set rfd;
char *argv0 = NULL;
int sem = -1;
struct sembuf free_sem = {0, 1, 0};
/* -------------------------------------------------------------------------- */
int main (int argc, char *argv[]) {
struct winsize win;
struct termios tt;
int i, bl, cc, n, ch;
int fflg = 0; /* spawn, fork */
int wflg = 0; /* watch for string [respond] */
int sflg = 0; /* send */
int kflg = 0; /* kill */
int lflg = 0; /* list your jobs */
int iflg = 0; /* in */
int oflg = 0; /* out */
int Sflg = 0; /* Strip last character from input */
int cflg = 0; /* use stdin instead of FIFO */
int vflg = 0; /* kvazi verbose mode OFF */
int timeout = 10; /* wait N secs for the responce */
int Lflg = 0; /* Log empty session */
int rflg = 0; /* recv output */
int bflg = 0; /* block size for -r flag */
int tflg = 0; /* Timeout flag for -b (timeout?) */
int pflg = 0; /* Shall we save PID to a file? */
long bs = 1;
int ksig = SIGTERM;
pid_t ppid; /* Shell's PID */
char infifo[MAXPATHLEN];
char outfifo[MAXPATHLEN];
struct sembuf check_sem = {0, -1, 0};
key_t sem_key;
const char *sem_file = "/";
time_t stime, ntime;
struct timeval tv;
/* semaphores */
#ifdef _POSIX_SEMAPHORES
#if defined(__linux__) && defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
union semun {
int val;
struct semid_ds *buf;
#ifdef __SVR4
ushort_t *array;
#endif
#ifdef __hpux__
ushort *array;
#endif
#ifdef __linux__
unsigned short *array;
struct seminfo *__buf; /* buffer for IPC_INFO */
#endif
};
#endif
#endif
union semun semu;
#if defined(__SVR4) || defined(__hpux__) || defined(__AIX)
char *slave_name;
int pgrp;
#endif
#ifndef __linux__
while ((ch = getopt(argc, argv, "Scvhfrb:kwslp:i:o:t:L:")) != -1)
#else
while ((ch = getopt(argc, argv, "+Scvhfrb:kwslp:i:o:t:L:")) != -1)
#endif
switch (ch) {
case 'f':
fflg = 1;
break;
case 'k':
/* Send signal */
kflg = 1;
break;
case 'w':
wflg = 1;
break;
case 's':
sflg = 1;
break;
case 'l':
lflg = 1;
break;
case 'i':
in = optarg;
iflg = 1;
break;
case 'o':
out = optarg;
oflg = 1;
break;
case 'p':
pfile = optarg;
pflg = 1;
break;
case 'r':
rflg = 1;
in = optarg;
break;
case 'b':
bflg = 1;
if ((bs = toint(optarg)) < 1) {
fprintf(stderr,
"Fatal: wrong -b value\n");
(void)usage();
}
break;
case 't':
/* wait N secs for the responce. Use with -w, -r */
tflg = 1;
if ((timeout = (int)toint(optarg)) < 1) {
fprintf(stderr,
"Fatal: wrong -t value\n");
(void)usage();
}
break;
case 'L':
/* Log session */
sl = optarg;
Lflg = 1;
break;
case 'c':
/* use stdin instead of FIFO */
cflg = 1;
break;
case 'S':
/* Strip last character from input */
Sflg = 1;
break;
case 'v':
vflg = 1;
break;
case 'h':
default:
(void)usage();
}
argc -= optind;
argv += optind;
if ((fflg + kflg + wflg + sflg + lflg + rflg) != 1)
(void)usage();
ppid = getppid();
if (kflg) { /* kill PID with the SIGNAL */
if (argv[0]) {
if ((pid = toint(argv[0])) < 0) {
fprintf(stderr, "Fatal: wrong PID value\n");
(void)usage();
}
/* Signal */
if (argv[1])
ksig = (int)toint(argv[1]);
} else
if ((pid = pidbyppid(ppid, lflg)) < 1)
(void)perrx(255, "Can't find desired process");
if (pid > 0 && (kill(pid, ksig) == -1))
(void)perrx(255, "Can't kill PID: %d", pid);
(void)exit(0);
}
if (lflg) {
pidbyppid(ppid, lflg);
(void)exit(0);
}
if (sflg) { /* we want to send */
if (!oflg) {
if ((pid = pidbyppid(ppid, lflg)) > 0) {
snprintf(outfifo, sizeof(outfifo), "%s/%s.%ld.%ld.in",
tmpdir, program, (long)ppid, (long)pid);
out = (char *)outfifo;
} else
(void)perrx(255, "Fatal can't find IN FIFO file by PPID:PID pair");
}
if ((ofd = open(out, O_WRONLY)) == -1)
(void)perrx(255, "Fatal open FIFO for writing: %s", out);
if (!cflg && argv[0] != NULL) {
bl = parsestr(buf, argv[0], strlen(argv[0]), Sflg);
if (write(ofd, buf, bl) == -1)
(void)perrx(255, "Fatal write data to FIFO: %s", out);
} else
while ((cc = read(0, buf, sizeof(buf))) > 0) {
if (cc == -1)
(void)perrx(255, "Fatal read from STDIN to buffer");
bl = parsestr(buf, buf, cc, Sflg);
if (write(ofd, buf, bl) == -1)
(void)perrx(255, "Fatal write STDIN data to FIFO: %s", out);
}
(void)exit(0);
}
if (rflg) {
if (!iflg) {
if ((pid = pidbyppid(ppid, lflg)) > 0) {
snprintf(infifo, sizeof(infifo), "%s/%s.%ld.%ld.out",
tmpdir, program, (long)ppid, (long)pid);
in = (char *)infifo;
} else
(void)perrx(255, "Fatal can't find OUT FIFO file by PPID:PID pair");
}
if ((ifd = open(in, O_RDONLY)) == -1)
(void)perrx(255, "Fatal open FIFO for reading: %s", in);
FD_ZERO(&rfd);
stime = time(0);
tv.tv_sec = timeout;
tv.tv_usec = 0;
cc = -1; while (cc != 0) {
FD_SET(ifd, &rfd);
n = select(ifd + 1, &rfd, 0, 0, &tv);
if (n < 0 && errno != EINTR)
perrx(255, "Fatal select()");
if (FD_ISSET(ifd, &rfd)) {
if ((cc = read(ifd, buf, bs)) == -1)
(void)perrx(255, "Fatal read from IN FIFO");
if (write(1, buf, cc) == -1)
(void)perrx(255, "Fatal write to STDOUT");
if ((!bflg && buf[0] == '\n') || bflg)
break;
} else
if (tflg) {
ntime = time(0);
if ((ntime - stime) >= timeout) {
(void)fprintf(stderr,
"%s: Buffer is empty. Exit on timeout\n",
program);
(void)close(ifd);
(void)exit(255);
}
}
}
(void)close(ifd);
(void)exit(0);
}
if (argc == 0)
(void)usage();
/* Otpion -w in order to get keyphrases and send responses */
if (wflg) {
if (!iflg && !oflg) {
if ((pid = pidbyppid(ppid, lflg)) > 0) {
sprintf(infifo, "%s/%s.%ld.%ld.out",
tmpdir, program, (long)ppid, (long)pid);
sprintf(outfifo, "%s/%s.%ld.%ld.in",
tmpdir, program, (long)ppid, (long)pid);
in = (char *)infifo;
out = (char *)outfifo;
} else
(void)perrx(255, "Fatal can't find FIFO files by PPID:PID pair");
}
if ((ifd = open(in, O_RDONLY)) == -1)
(void)perrx(255, "Fatal open FIFO for reading: %s", in);
if ((ofd = open(out, O_WRONLY)) == -1)
(void)perrx(255, "Fatal open FIFO for writing: %s", out);
checkgr(argc, argv, NULL, 1); /* check regexp syntax only */
(void)exit(watch4str(ifd, ofd, argc, argv, Sflg, vflg, cflg, timeout));
}
(void)openlog(program, 0, 0);
(void)syslog(LOG_NOTICE, "version %s started", version);
argv0 = argv[0];
(void)tcgetattr(STDIN_FILENO, &tt);
(void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
if ((sem_key = ftok(sem_file, getpid())) == -1)
(void)perrxslog(255, "Can't generate semaphore key from file %s %m", sem_file);
if ((sem = semget(sem_key, 1, 0600 | IPC_CREAT)) == -1)
(void)perrxslog(255, "Can't get semaphore %m");
semu.val = 0;
if (semctl(sem, 0, SETVAL, semu) == -1)
(void)perrxslog(255, "Can't set semaphore %d to lock: %m", sem);
if ((pid = fork()) == -1)
(void)perrxslog(255, "Daemonizing failed. Fatal first fork");
if (pid > 0) {
if ((semop(sem, &check_sem, 1) == -1) && (errno != EINVAL) && (errno != EIDRM))
/* Semaphore was removed by the child */
(void)perrxslog(255, "Can't get semaphore %d status: %m", sem);
(void)exit(0);
}
if (setsid() < 0)
(void)perrxslog(255, "Daemonizing failed. Fatal setsid");
if ((pid = fork()) == -1)
(void)perrxslog(255, "Daemonizing failed. Fatal second fork");
/* Fork does not work properly */
if (pid > 0)
(void)exit(0);
for (i = 1; i < 32; i++)
#if defined(__AIX) || defined(__hpux__) || defined(__OSF1) || defined(__SVR4) && !defined(__SCO_VERSION__)
if (i != SIGCHLD)
#endif
signal(i, fsignal);
pid = getpid(); /* MY PID */
if (pflg) {
if ((pfd = open(pfile, O_CREAT|O_WRONLY, S_IRWXU)) == -1)
(void)syslog(LOG_NOTICE,
"Warning: Can't write pid-file %s %m", pfile);
pf = fdopen(pfd, "w");
fprintf(pf, "%ld\n", (long)pid);
fclose(pf);
}
if (!(iflg && oflg)) {
sprintf(infifo, "%s/%s.%ld.%ld.in", tmpdir, program, (long)ppid, (long)pid);
sprintf(outfifo, "%s/%s.%ld.%ld.out", tmpdir, program, (long)ppid, (long)pid);
in = (char *)infifo;
out = (char *)outfifo;
}
if (Lflg)
if ((lfd = open(sl, O_CREAT|O_WRONLY|O_APPEND, S_IRWXU)) == -1)
(void)syslog(LOG_NOTICE,
"Warning: Can't open %s for session-log %m", sl);
if ((ifd = mfifo(in, O_RDWR)) == -1 && errno != ENXIO)
(void)perrxslog(255, "Fatal creating FIFO: %s", in);
if ((ofd = mfifo(out, O_RDWR)) == -1 && errno != ENXIO)
(void)perrxslog(255, "Fatal creating FIFO: %s", out);
if (semop(sem, &free_sem, 1) == -1)
(void)perrxslog(255, "Can't release semaphore: %d from lock %m", sem);
if (semctl(sem, 0, IPC_RMID) == -1)
(void)syslog(LOG_NOTICE, "Warning: Can't remove semaphore: %d %m", sem);
#if !defined(__SVR4) && !defined(__hpux__) && !defined(__AIX)
if (openpty(&master, &slave, NULL, &tt, &win) == -1)
(void)perrxslog(255, "PTY routine failed. Fatal openpty()");
#else
#ifdef __AIX
if ((master = open("/dev/ptc", O_RDWR | O_NOCTTY)) == -1)
(void)perrxslog(255, "PTY routine failed. Fatal open(\"/dev/ptc\"), ...");
#else
if ((master = open("/dev/ptmx", O_RDWR)) == -1)
(void)perrxslog(255, "PTY routine failed. Fatal open(\"/dev/ptmx\"), ...");
#endif
#ifdef __hpux__
/* See the same definition for Solaris & UW several lines below */
if (grantpt(master) == -1)
(void)perrxslog(255, "Can't grant access to slave part of PTY: %m");
/* grantpt() may fail if SIGCHLD was previously set */
signal(SIGCHLD, fsignal);
#endif
if (unlockpt(master) == -1)
(void)perrxslog(255, "PTY routine failed. Fatal unlockpt()");
if ((slave_name = (char *)ptsname(master)) == NULL)
(void)perrxslog(255, "PTY routine failed. Fatal ptsname(master)");
#if defined(__SVR4) && !defined(__SCO_VERSION__)
if (grantpt(master) == -1)
(void)perrxslog(255, "Can't grant access to slave part of PTY: %m");
/* grantpt() may fail if SIGCHLD was previously set */
signal(SIGCHLD, fsignal);
#endif
#endif /* !defined(__SVR4) && !defined(__hpux__) && !defined(__AIX) */
if ((child = fork()) < 0) {
(void)clean();
(void)perrxslog(255, "Fatal fork at creating desired process");
}
/* If this is the main process : launch with -f */
if (child == 0) {
(void)close(master);
#if !defined(__SVR4) && !defined(__hpux__) && !defined(__AIX)
login_tty(slave);
#ifndef __CYGWIN__
cfmakeraw(&tt);
#endif
#else
if ((pgrp = setsid()) == -1)
(void)syslog(LOG_NOTICE, "Warning: Can't setsid(): %m");
if ((slave = open(slave_name, O_RDWR)) == -1)
(void)perrxslog(255, "Fatal open slave part of PTY %s", slave_name);
#ifndef __AIX
ioctl(slave, I_PUSH, "ptem");
ioctl(slave, I_PUSH, "ldterm");
ioctl(slave, I_PUSH, "ttcompat");
#endif
/* Duplicate open file descriptor */
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
/* Set foreground the main process */
if (tcsetpgrp(0, pgrp) == -1)
(void)perrxslog(255, "Fatal tcsetpgrp()");
#endif
tt.c_lflag &= ~ECHO;
(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
strncpy(buf, argv[0], sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
for (i = 1; i < argc; i++) {
strncat(buf, " ", 1);
strncat(buf, argv[i], sizeof(buf));
}
(void)syslog(LOG_NOTICE, "forked %s", buf);
/* Exec and quit if successful */
execvp(argv[0], argv);
(void)syslog(LOG_NOTICE, "Failed loading %s: %m", buf);
(void)clean();
(void)kill(0, SIGTERM);
(void)exit(0);
}
/* If this is the forked process */
FD_ZERO(&rfd);
for (;;) {
FD_SET(master, &rfd);
FD_SET(ifd, &rfd);
n = select(master + 1, &rfd, 0, 0, NULL);
if (n > 0 || errno == EINTR) {
if (FD_ISSET(ifd, &rfd))
if ((cc = read(ifd, buf, sizeof(buf))) > 0) {
(void)write(master, buf, cc);
if (lfd)
(void)write(lfd, buf, cc);
}
if (FD_ISSET(master, &rfd))
if ((cc = read(master, buf, sizeof(buf))) > 0) {
(void)write(ofd, buf, cc);
if (lfd)
(void)write(lfd, buf, cc);
}
}
}
/* This never reached */
/* return 0; */
}
/* -------------------------------------------------------------------------- */
static void usage(void) {
(void)fprintf(stderr,
"%s-%s usage:\n\
empty -f [-i fifo1 -o fifo2] [-p file.pid] [-L file] command [command args]\n\
empty -w [-Sv] [-t n] [-i fifo2 -o fifo1] key1 [answer1] ... [keyX answerX]\n\
empty -s [-Sc] [-o fifo1] [request]\n\
empty -r [-b size] [-t n] [-i fifo1]\n\
empty -l\n\
empty -k [pid] [signal]\n\
empty -h\n", program, version);
exit(255);
}
/* -------------------------------------------------------------------------- */
long toint(char *intstr) {
int in;
in = strtol(intstr, (char **)NULL, 10);
if (in == 0 && errno == EINVAL) {
fprintf(stderr, "Wrong long value: %s\n", intstr);
usage();
}
return in;
}
/* -------------------------------------------------------------------------- */
long pidbyppid(pid_t ppid, int lflg) {
char fmask[MAXPATHLEN];
char fname[MAXPATHLEN];
const char *sep = ".";
DIR *dir;
struct dirent *dent;
int len;
char *chpid, *tail;
long pid = -1, maxpid = -1;
int header = 1;
/* form this line: empty.ppid */
sprintf(fmask, "%s%s%d", program, sep, ppid);
len = strlen(fmask);
if ((dir = opendir(tmpdir)) == NULL)
(void)perrx(255, "Can't open directory: %s", tmpdir);
while ((dent = readdir(dir)) != NULL) {
if (!strncmp(fmask, dent->d_name, len)) {
strncpy(fname, dent->d_name, sizeof(fname) - 1);
fname[sizeof(buf) - 1] = '\0';
strtok(fname, sep); /* empty */
strtok(NULL, sep); /* PPID */
chpid = strtok(NULL, sep); /* PID */
tail = strtok(NULL, sep); /* IN or OUT */
if (chpid != NULL) {
pid = toint(chpid);
pid > maxpid ? maxpid = pid : pid;
}
if (lflg) {
if (header) {
printf("PPID\tPID\tTYPE\tFILENAME\n");
header --;
}
printf("%ld\t%ld\t%s\t%s/%s\n",
(long)ppid, (long)pid, tail, tmpdir, dent->d_name);
}
}
}
if (closedir(dir) == -1)
(void)perror("Warning closing directory");
if (lflg && pid > 0)
printf("\n%ld\t%ld\tcurrent\n", (long)ppid, (long)maxpid);
return maxpid;
}
/* -------------------------------------------------------------------------- */
void wait4child(int child, char *argv0) {
while ((pid = wait3(&status, WNOHANG, 0)) > 0)
if (pid == child)
(void)syslog(LOG_NOTICE, "%s exited", argv0);
}
/* -------------------------------------------------------------------------- */
int mfifo(char *fname, int mode) {
if (mkfifo(fname, S_IFIFO|S_IRWXU) == -1)
return -1;
return open(fname, mode);
}
/* -------------------------------------------------------------------------- */
void clean(void) {
(void)close(master);
(void)close(ifd);
(void)close(ofd);
(void)close(lfd);
(void)unlink(in);
(void)unlink(out);
(void)unlink(pfile);
}
/* -------------------------------------------------------------------------- */
void perrx(int ex_code, const char *err_text, ...) {
char err_buf[BUFSIZ];
va_list va;
va_start(va, err_text);
vsnprintf(err_buf, sizeof(err_buf), err_text, va);
va_end(va);
(void)perror(err_buf);
(void)exit(ex_code);
}
/* -------------------------------------------------------------------------- */
void perrxslog(int ex_code, const char *err_text, ...) {
va_list va;
va_start(va, err_text);
#if !defined(__hpux__) && !defined(__AIX) && !defined(__OSF1)
(void)vsyslog(LOG_NOTICE, err_text, va);
#else
char err_buf[BUFSIZ];
vsprintf(err_buf, err_text, va);
(void)syslog(LOG_NOTICE, err_buf, "");
#endif
va_end(va);
if (sem != -1)
semctl(sem, 0, IPC_RMID);
(void)closelog();
(void)exit(ex_code);
}
/* -------------------------------------------------------------------------- */
void fsignal(int sig) {
switch(sig) {
case SIGTERM:
case SIGINT:
case SIGQUIT:
case SIGSEGV:
break;
case SIGCHLD:
wait4child(child, argv0);
(void)syslog(LOG_NOTICE,
"version %s finished", version);
}
(void)clean();
semop(sem, &free_sem, 1);
(void)closelog();
(void)exit(0);
}
/* -------------------------------------------------------------------------- */
int longargv(int argc, char *argv[]) {
int i = 0, len = 0, maxlen = 0;
while (argv[i] != NULL) {
len = strlen(argv[i]);
if (len > maxlen)
maxlen = len;
i++;
}
return maxlen;
}
/* -------------------------------------------------------------------------- */
int checkgr(int argc, char *argv[], char *buf, int chkonly) {
int i;
regex_t re;
for (i = 1; i <= argc; i++) {
if (regcomp(&re, argv[i - 1], REG_EXTENDED | REG_NOSUB) != 0)
(void)perrx(255, "Regex compilation failed");
if (chkonly != 1)
switch (regmatch(buf, argv[i - 1], &re)) {
case 1: /* match */
return i;
case 0: /* not found, check next key */
if ((i + 1) <= argc)
i++;
break;
}
}
return 0; /* nothing found */
}
/* -------------------------------------------------------------------------- */
int regmatch(const char *string, char *pattern, regex_t *re) {
int status;
/* regcomp() is not needed as it was previously executed by checkgr() */
status = regexec(re, string, (size_t) 0, NULL, 0);
regfree(re);
switch (status) {
case 0:
return(1);
case REG_NOMATCH:
return(0);
default:
(void)perrx(255, "Regex execution failed");
}
return(255); /* Not reached */
}
/* -------------------------------------------------------------------------- */
int watch4str(int ifd, int ofd, int argc, char *argv[],
int Sflg, int vflg, int cflg, int timeout) {
int n, cc, bl;
time_t stime, ntime;
struct timeval tv;
int argt = 0;
int largv = 0;
char *resp = NULL;
stime = time(0);
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO(&rfd);
for (;;) {
FD_SET(ifd, &rfd);
n = select(ifd + 1, &rfd, 0, 0, &tv);
if (n < 0 && errno != EINTR)
perrx(255, "Fatal select()");
if (FD_ISSET(ifd, &rfd)) {
largv = 0;
if ((cc = read(ifd, buf + largv, sizeof(buf) - largv)) > 0) {
stime = time(0);
buf[cc + largv] = '\0';
if (vflg)
(void)printf("%s", buf);
if ((argt = checkgr(argc, argv, buf, 0)) > 0) {
if ((resp = argv[argt])) {
/* Nb chars for buf */
bl = parsestr(buf, resp, strlen(resp), Sflg);
/* write response to fifo */
if (write(ofd, buf, bl) == -1)
(void)perrx(255, "Fatal write data to output");
}
/* exit program */
return (argt + 1) / 2;
}
if (largv == 0)
largv = longargv(argc, argv);
memmove(buf, buf + cc - largv, largv);
}
if (cc <= 0) {
/* Got EOF or ERROR */
if (vflg)
(void)fprintf(stderr, "%s: Got nothing in output\n",
program);
return 255;
}
}
ntime = time(0);
if ((ntime - stime) >= timeout) {
(void)fprintf(stderr,
"%s: Data stream is empty. Keyphrase wasn't found. Exit on timeout\n",
program);
return 255;
}
}
}
/* -------------------------------------------------------------------------- */
int parsestr(char *dst, char *src, int len, int Sflg) {
int i, bi;
/* Return numbers of chars for response */
Sflg == 1 ? len-- : len;
for (i = 0, bi = 0; i < len; i++, bi++) {
if (src[i] == '\\')
switch (src[i + 1]) {
case '\\':
dst[bi] = '\\';
i++;
break;
case 'n':
dst[bi] = '\n';
i++;
break;
case 'r':
dst[bi] = '\r';
i++;
break;
default:
dst[bi] = src[i];
}
else
dst[bi] = src[i];
}
return bi;
}
/* -------------------------------------------------------------------------- */
syntax highlighted by Code2HTML, v. 0.9.1