empty-0.6.19b/000075500256230027355000000000001202632574600162475ustar00mezakharovstorageadmins00005120000064empty-0.6.19b/examples/000075500256230027355000000000001202632540200200525ustar00mezakharovstorageadmins00005120000064empty-0.6.19b/examples/em_ssh_2.sh000075500256230027355000000015261202632540200221140ustar00mezakharovstorageadmins00005120000064#!/bin/sh # emssh_2.sh v.0.5 # ssh example for empty with job control, STDIN mode and session log # Though, don't really know who wants to use empty for ssh :) # Copyright (C) 2005, 2006 Mikhail E. Zakharov # ssh="ssh" # (/full/path/to/)ssh target="localhost" # target host login="luser" # username (Change it!) password="TopSecret" # password (Change it!) # ----------------------------------------------------------------------------- cmd="$ssh $login@$target" tmp="/tmp/empty.tmp" # tempfile to store results echo "Starting empty" empty -f -L $tmp $cmd if [ $? = 0 ]; then echo "Sending Password" empty -v -w assword: "$password\n" echo "Sending tests" empty -s << EOF who am i id exit EOF echo "Check results:" sleep 1 cat $tmp rm -f $tmp else echo "Error: Can't start empty in daemon mode" return 1 fi echo "Done" empty-0.6.19b/examples/emsu.sh000075500256230027355000000020651202632540200213650ustar00mezakharovstorageadmins00005120000064#!/bin/sh # emsu.sh v.0.4 # su example for empty # Copyright (C) 2005, 2006 Mikhail E. Zakharov # password="RootPassword" # password (Change it!) fifo_in="/tmp/empty.in" # input fifo fifo_out="/tmp/empty.out" # output # ----------------------------------------------------------------------------- tmp="/tmp/empty.tmp" # tempfile to store results echo "Starting empty" empty -f -i $fifo_in -o $fifo_out su if [ $? = 0 ]; then sleep 1 # heh, we may need this sleep() again if [ -w $fifo_in -a -r $fifo_out ]; then echo "Sending Password" empty -w -v -i $fifo_out -o $fifo_in -t 5 assword: "$password\n" echo "Sending tests" empty -s -o $fifo_in "echo EMPTY TEST BEGIN\n > $tmp" empty -s -o $fifo_in "who am i\n >> $tmp" empty -s -o $fifo_in "id\n >> $tmp" empty -s -o $fifo_in "echo EMPTY TEST END\n >> $tmp" echo "Sending exit" empty -s -o $fifo_in 'exit\n' echo "Check results:" sleep 1 cat $tmp else echo "Error: Can't find I/O fifos!" return 1 fi else echo "Error: Can't start empty in daemon mode" return 1 fi echo "Done" empty-0.6.19b/examples/README000064400256230027355000000001501202632540200207260ustar00mezakharovstorageadmins00005120000064Execute ./runtests.sh to start endless test for empty. You need telnet and ssh login to your localhost. empty-0.6.19b/examples/em_ssh.sh000075500256230027355000000023741202632540200216750ustar00mezakharovstorageadmins00005120000064#!/bin/sh # emssh.sh v.0.5 # ssh example for empty # Though, don't really know who wants to use empty for ssh :) # Copyright (C) 2005, 2006 Mikhail E. Zakharov # ssh="ssh" # (/full/path/to/)ssh target="localhost" # target host login="luser" # username (Change it!) password="TopSecret" # password (Change it!) fifo_in="/tmp/empty.in" # input fifo fifo_out="/tmp/empty.out" # output # ----------------------------------------------------------------------------- cmd="$ssh $login@$target" tmp="/tmp/empty.tmp" # tempfile to store results echo "Starting empty" empty -f -L $tmp -i $fifo_in -o $fifo_out $cmd if [ $? = 0 ]; then if [ -w $fifo_in -a -r $fifo_out ]; then echo "Sending Password" empty -w -v -i $fifo_out -o $fifo_in -t 5 assword: "$password\n" echo "Sending tests" empty -s -o $fifo_in "echo \"-- EMPTY TEST BEGIN --\"\n" empty -s -o $fifo_in "uname -a\n" empty -s -o $fifo_in "uptime\n" empty -s -o $fifo_in "who am i\n" empty -s -o $fifo_in "echo \"-- EMPTY TEST END --\"\n" echo "Sending exit" empty -s -o $fifo_in 'exit\n' echo "Check results:" sleep 1 cat $tmp rm -f $tmp else echo "Error: Can't find I/O fifos!" return 1 fi else echo "Error: Can't start empty in daemon mode" return 1 fi echo "Done" empty-0.6.19b/examples/em_sh.sh000075500256230027355000000011121202632540200214770ustar00mezakharovstorageadmins00005120000064#!/bin/sh # emsh.sh v.0.4 # sh example for empty with job control and STDIN mode # Copyright (C) 2005, 2006 Mikhail E. Zakharov # # ----------------------------------------------------------------------------- tmp="/tmp/empty.tmp" # tempfile to store results echo "Starting empty" empty -f -L $tmp sh if [ $? = 0 ]; then echo My jobs: empty -l echo "Sending tests" empty -s << EOF echo -- EMPTY TEST BEGIN -- who am i id echo -- EMPTY TEST END -- sleep 1 exit EOF sleep 1 cat $tmp rm -f $tmp else echo "Error: Can't start empty in daemon mode" return 1 fi echo "Done" empty-0.6.19b/examples/runtests.sh000075500256230027355000000003521202632540200223000ustar00mezakharovstorageadmins00005120000064#!/bin/sh echo "Starting endless tests" while true; do for i in em_*; do echo "== Starting: $i ================================================" ./$i if [ $? != 0 ]; then echo "Error at test $i" exit 1 fi done done empty-0.6.19b/examples/em_telnet_2.sh000075500256230027355000000026361202632540200226150ustar00mezakharovstorageadmins00005120000064#!/bin/sh # emtelnet.sh v.0.4 # telnet example for empty # Copyright (C) 2005, 2006 Mikhail E. Zakharov # telnet="telnet" # (/full/path/to/)telnet target="localhost" # target telnet-host login="luser" # username (Change it!) password="TopSecret" # password (Change it!) fifo_in="/tmp/empty.in" # input fifo fifo_out="/tmp/empty.out" # output # telnet command examples. Chose one: telnet_cmd="$telnet -K $target" # connect FreeBSD from FreeBSD (SRA) #telnet_cmd="$telnet $target" # All other OSes # ----------------------------------------------------------------------------- tmp="/tmp/empty.tmp" # tempfile to store result echo "Starting empty" empty -f -i $fifo_in -o $fifo_out -L $tmp $telnet_cmd if [ $? = 0 ]; then if [ -w $fifo_in -a -r $fifo_out ]; then empty -w -v -i $fifo_out -o $fifo_in -t 5 ogin: "$login\n" echo Keyphrase matches : $? empty -w -v -i $fifo_out -o $fifo_in -t 5 "qwerrtry" "qwqwqw" assword: "$password\n" echo Keyphrase matches : $? empty -s -o $fifo_in "echo -- EMPTY TEST BEGIN --\n" empty -s -o $fifo_in "uname -a\n" empty -s -o $fifo_in "uptime\n" empty -s -o $fifo_in "who am i\n" empty -s -o $fifo_in "echo -- EMPTY TEST END --\n" empty -s -o $fifo_in 'exit\n' echo "Check results:" sleep 1 cat $tmp rm -f $tmp else echo "Error: Can't find I/O fifos!" return 1 fi else echo "Error: Can't start empty in daemon mode" return 1 fi echo "Done" empty-0.6.19b/examples/em_telnet.sh000075500256230027355000000026631202632540200223740ustar00mezakharovstorageadmins00005120000064#!/bin/sh # emtelnet.sh v.0.4 # telnet example for empty # Copyright (C) 2005, 2006 Mikhail E. Zakharov # telnet="telnet" # (/full/path/to/)telnet target="localhost" # target telnet-host login="luser" # username (Change it!) password="TopSecret" # password (Change it!) fifo_in="/tmp/empty.in" # input fifo fifo_out="/tmp/empty.out" # output # telnet command examples. Chose one: telnet_cmd="$telnet -K $target" # connect FreeBSD from FreeBSD (SRA) #telnet_cmd="$telnet $target" # All other OSes # ----------------------------------------------------------------------------- tmp="/tmp/empty.tmp" # tempfile to store result echo "Starting empty" empty -f -i $fifo_in -o $fifo_out -L $tmp $telnet_cmd if [ $? = 0 ]; then if [ -w $fifo_in -a -r $fifo_out ]; then echo "Sending Login" empty -w -v -i $fifo_out -o $fifo_in -t 5 ogin: "$login\n" echo "Sending Password" empty -w -v -i $fifo_out -o $fifo_in -t 5 assword: "$password\n" echo "Sending tests" empty -s -o $fifo_in "echo \"-- EMPTY TEST BEGIN --\"\n" empty -s -o $fifo_in "uname -a\n" empty -s -o $fifo_in "uptime\n" empty -s -o $fifo_in "who am i\n" empty -s -o $fifo_in "echo \"-- EMPTY TEST END --\"\n" echo "Sending exit" empty -s -o $fifo_in 'exit\n' echo "Check results:" sleep 1 cat $tmp rm -f $tmp else echo "Error: Can't find I/O fifos!" return 1 fi else echo "Error: Can't start empty in daemon mode" return 1 fi echo "Done" empty-0.6.19b/Makefile000064400256230027355000000016111202632540200176730ustar00mezakharovstorageadmins00005120000064# # Makefile for empty # # Usage: # make all install clean # or # make `uname -s` install clean # or # make `uname -s`-gcc install clean # CC = gcc LIBS = -lutil PREFIX = /usr/local all: ${CC} ${CFLAGS} -Wall ${LIBS} -o empty empty.c FreeBSD: all NetBSD: all OpenBSD: all Linux: all Cygwin: all UnixWare: SunOS OpenUNIX: SunOS AIX: SunOS OSF1: SunOS HP-UX: SunOS SunOS: cc -o empty empty.c UnixWare-gcc: SunOS-gcc OpenUNIX-gcc: SunOS-gcc HP-UX-gcc: SunOS-gcc SunOS-gcc: gcc ${CFLAGS} -Wall -o empty empty.c install: [ -f `which strip` ] && strip empty [ -d ${PREFIX}/bin ] && cp empty ${PREFIX}/bin || mkdir -p ${PREFIX}/bin && cp empty ${PREFIX}/bin [ -d ${PREFIX}/man/man1 ] && cp empty.1 ${PREFIX}/man/man1 || mkdir -p ${PREFIX}/man/man1 && cp empty.1 ${PREFIX}/man/man1 deinstall: rm ${PREFIX}/bin/empty rm ${PREFIX}/man/man1/empty.1 uninstall: deinstall clean: rm empty empty-0.6.19b/empty.1000064400256230027355000000204611202632540200174570ustar00mezakharovstorageadmins00005120000064.TH empty 1 "March, 05 2006" .SH NAME empty \- run processes under pseudo-terminal sessions .SH SYNOPSIS .br .B empty \-f [\-i fifo1 \-o fifo2] [-p file.pid] [\-L file.log] command [command args] .br .B empty \-w [\-Sv] [\-t n] [\-i fifo2 \-o fifo1] key1 [answer1] ... [keyX answerX] .br .B empty \-s [\-Sc] \-o fifo1 [request] .br .B empty \-r [\-b size] [\-t n] [\-i fifo2] .br .B empty \-l .br .B empty \-k [pid] [signal] .br .B empty \-h .SH DESCRIPTION .B empty is an utility that provides a simple interface to execute and/or interact with processes under pseudo-terminal sessions. This tool is definitely useful in programming of shell scripts which are used to communicate with interactive programs like telnet or ftp. In some cases .B empty can be a substitution for TCL/expect or other similar programming tools. .PP There are several common forms of command lines for .B empty. But the first execution of .B empty is usually a start in the daemon mode to .B fork a new .B command (process) under PTY-session. This can be done with .B \-f key. An interface for the input and output data channels of the forked process is performed by two fifo files which names may be specified with .B \-i and .B \-o keys. These files are automatically created/deleted any time you start/exit .B empty daemon, so you must not create them manually. If you did not specify these fifo files in the command line, .B empty names them by itself basing on its PID and PID of forked PTY process. .PP At this point any application can easily communicate with forked process by writing data to the input fifo and reading answers from the output fifo, see .B EXAMPLES section for the details. To simplify this operations, .B empty offers an interfase to just .B send any data (use .B \-s key), or even to .B watch the output fifo for multiple .B keyphrases and reply to the input fifo with one of the .B responses (see .B \-w key). .PP .B Note! Input fifo for .B empty -f ... is usually an output fifo for .B empty -w and .B empty -s forms. And output fifo of .B empty -f ... is an input fifo for .B empty -w ... .PP If something goes wrong the forked process may be .B killed by the standard kill command, or using .B \-k key of .B empty. See .B \-p option to save PID of empty daemon process. .PP The following options are available: .TP 12 \-f fork, spawn, start or execute a new process specified by the .B command and its arguments. If you omit fifo files, .B empty with its job control algorithm will create them under /tmp directory using this templates: empty.PPID.PID.in and empty.PPID.PID.out, here PPID is usually your shell system process ID and PID is system process ID of empty-daemon. .TP \-s send data (request) to the forked process. If fifo file was not specitied with .B \-o key, .B empty will try to find an automatically created fifo file it in /tmp directory. Instead of command line you can send your .B request or data directly to standard input (stdin) of .B empty .TP \-w .B watch for one or more .B keyphrases and if specified send the appropriated .B response to the input fifo. If .B response is not set, .B empty waits for the proper .B keyphrase then exits. With .B \-w key .B empty returns the number of matched keyphrase-response pair, or 255 if fails to find this match (see -t key for details of possible exit on timeout). .TP \-r read from output FIFO one line (default) or one block of data (if .B \-b size was specified). If .B \-t n key was placed, exit on timeout. .TP \-l list automatically created jobs by your shell. .B NB! Your custom jobs, which fifo files you specified with .B \-i and .B \-o keys, are not displayed. So if you did not specify fifo files with .B -i and .B -o keys all operations are done under the job marked .B current .TP \-k send .B signal to the process with .B pid. If you did not specify pid, .B empty tries to find it within the list of automatically created jobs. If .B signal is omitted the default SIGTERM is used. .TP \-h print short help message and exit .TP \-i fifo1 a fifo file, which is used as input for a forked process. .TP \-o fifo2 a fifo file, which is used as output for a forked process. .TP \-L file.log This option allows to log the whole .B empty session to a file. Marks >>> and <<< show the directions of data flow. .TP \-p file.pid Save PID of empty daemon process to a file .TP \-t n If input FIFO is empty, wait for n seconds (default is 10) to receive the .B keyphrase then exit on timeout with 255 code. .TP \-c force .B empty to use stdin for data or requests. .TP \-S Strip the last character from the input. Works with -s and -w keys .TP \-v kvazi verbose mode. Show all contents of received buffer. .SH EXAMPLES .TP 0 Start a new PTY-session with telnet to localhost: .nf empty -f -i in.fifo -o out.fifo -p empty.pid -L empty.log telnet localhost .fi .TP Interact with telnet: .nf empty \-w \-i out.fifo \-o in.fifo ogin 'my_user\\n' empty \-w \-i out.fifo \-o in.fifo assword 'my_password\\n' .fi .TP Send commands to telnet with empty: .nf empty \-s \-o in.fifo who empty \-s \-o in.fifo "ls \-la /\\n" .fi .TP The same using STDIN: .nf echo who | empty \-s \-o in.fifo echo "ls \-la /" | empty \-s \-o in.fifo .fi .TP Just cat output from telnet: .nf cat out.fifo .fi .TP Read one line from out.fifo: .nf empty \-r -i out.fifo .fi .TP Send commands to telnet with ordinary echo: .nf echo "who am i" > in.fifo echo "uname -a" > in.fifo .fi .TP Kill a process with PID 1234: .nf empty -k 1234 or kill 1234 .nf .TP Telnet session with automatically created jobs: .nf empty -f telnet localhost .fi .TP Interact with telnet using job control: .nf empty \-w ogin 'my_user\\n' empty \-w assword 'my_password\\n' .fi .TP List automatically created jobs: .nf empty \-l PPID PID TYPE FILENAME 479 706 in /tmp/empty.479.706.in 479 706 out /tmp/empty.479.706.out 479 711 in /tmp/empty.479.711.in 479 711 out /tmp/empty.479.711.out 479 711 current .fi .SH SECURITY It is considered insecure to send a password in the command line like this: .nf empty \-w assword 'my_password\\n' .fi or like this: .nf empty \-s 'my_password\\n' .fi The reason is that the command line arguments are visible to the system while .B empty is running. Any local user can see them with ps(1), sometimes they are visible even remotely with finger(1). Also your server may have some monitoring tools which may store the output from ps(1) in their logs. There are also other, more complicated ways to compromise this information. Generally, you should take command line arguments as (possibly) visible to every one unless you really know what you're doing. .B empty with '-s' flag runs quickly in most cases, but still it can hang for a number of reasons (like fifo overloading), and even if it runs quick you still cannot be sure that no one will see its command line arguments even in this short time. .B empty with '-w' flag is even worse because it must wait for the keyphrase. A better way to send the password to the supervised program is to read it from file: .nf empty \-s [common options] <./password-file .fi or from a pipe: .nf get-password-of-user "$user" |empty -s [common options] .fi You should still make sure that you do not send any password via command line while creating this file, and certainly you should set some safe permissions to this file AND its directory (with the parent directories) before reading the password from the file OR writing the password to it. Another possible way is to use your shell's builtin (but see below): .nf echo "$password" |empty \-s [common options] .fi Many shells like bash(1), csh(1) and FreeBSD's sh(1) do not call external echo(1) command but use their own builtin echo command. Since no external command is started (the shell itself does all that echo(1) must do), nothing is shown in the process list. It is beyond this manual page to discuss the way to make sure that your shell uses the builtin command. .SH RETURN VALUES If any error occurs .B empty usually exits with code 255. Otherwise zero or some positive value (see .B \-w key) is returned. .SH SEE ALSO expect(1), chat(8) .SH AUTHOR .B empty was made 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. SECURITY section of this manual page was contributed by Sergey Redin. empty-0.6.19b/README.CYGWIN000064400256230027355000000006101202632540200201100ustar00mezakharovstorageadmins00005120000064To run empty on Cygwin Linux-like environment you have to do make this steps: 1. Download and install cygserver (http://www.cygwin.com/cygwin-ug-net/using-cygserver.html) 2. Run /usr/bin/cygserver-config to configure cygserver 3. Start Windows service (CYGWIN cygserver) 4. Add value "server" to CYGWIN environment variable (for example: export CYGWIN=server) 5. Start empty in a common way empty-0.6.19b/README000064400256230027355000000037341202632566700171400ustar00mezakharovstorageadmins00005120000064empty-0.6.19b -- run applications under pseudo-terminal (PTY) sessions and replace TCL/Expect with a simple tool under your favorite shell (sh, bash, csh, tcsh, ksh, zsh, etc) WARNING! -------------------------------------------------------------------------------- This version of the software has BUGS, so be careful and use it on you own risk! -------------------------------------------------------------------------------- INSTALLATION for all known *BSD systems and Linux run: make all make install clean on all other *UNIX try: make `uname -s` install clean or make `uname -s`-gcc install clean This will compile and place empty with its manual page under /usr/local tree. Empty was succesfully compiled and tested on: FreeBSD 5.x - 9.x NetBSD 1.6.2, 2.0 OpenBSD 3.3, 3.8, 4.1 Linux 2.4.x, 2.6.x kernels (32 and 64bits) SCO UnixWare 7.1.1, OpenUnix 8.0.0 SUN Solaris 6-10 (Sparc: cc, gcc), 8 (i386: gcc) HP-UX 11.00, 11.11, 11.23 (pa-risc, itanium) Cygwin DLL-1.5.19-4 (Experimental) OSF1 4.0, 5.1 AIX 4.3, 5.1, 5.2, 5.3 USAGE See empty(1) manual and samples under examples/ directory. BUGS Please, send all your reports, patches and suggestions to zmey20000@yahoo.com. Developers are welcome. THANKS TO Aleksey Kozlov Alexey Rudometov Alex Slyotov Bernd Schuller David Hofstee Dennis Schridde Dmitry S. Vlasov Elisender Lang Qiu Ralf Winkel Roman Gorohov sdio Sergey Redin Sergey V Sergey Yaroshevsky Sylvain DEGUT Vincenzo Maggio I express deep gratitude to Hewlett Packard AO Russia and personnaly to Denis Karzhavin and Andrew Kuchinsky for C110 (9000/777) workstation September 19, 2012 Mikhail Zakharov zmey20000@yahoo.com empty-0.6.19b/CHANGELOG000064400256230027355000000163221202632540200174520ustar00mezakharovstorageadmins0000512000006419.08.2012 0.6.19b -------------------------------------------------------------------------------- * Fix for non terminal execution on IBM AIX 5.3. Thanks to Ralf Winkel 27.07.2009 0.6.18b -------------------------------------------------------------------------------- * "Fatal select(): Success" Artifact under Linux fixed. Thanks Bernd Schuller and Dennis Schridde for discovering the bug 23.04.2009 0.6.17b -------------------------------------------------------------------------------- * Empty -w gets 100% cpu usage under Linux. Fixed. Thanks "David Hofstee" for the bugfix * Man-page -r section fixed 21.11.2008 0.6.16b -------------------------------------------------------------------------------- * Terminal emulation is more complex now. Great thanks: "lang qiu" who found the problem and the way to fix it under Solaris OS. "Alexey Rudometov" for reproduction of the bug and testing under Solaris * Wrong ${PREFIX} in Makefile from previous release fixed * Marks >>> and <<< added to logfile to indicate direction of stream * Man page updated 27.01.2008 0.6.15b -------------------------------------------------------------------------------- * Fixed fatal bug under FreeBSD-6.3 Release new PTY scheme. Thanks to "Sergey Yaroshevsky" for discovering the bug * Minor changes in Makefile to handle ${PREFIX} 29.11.2007 0.6.14b -------------------------------------------------------------------------------- * Trivial bug in handling of strncpy() functions fixed 24.10.2007 0.6.12b -------------------------------------------------------------------------------- * mfifo() bug under OpenBSD fixed * Empty is now fully functional under OpenBSD Great thanks to Vincenzo Maggio (vince.maggio@gmail.com) for deep debugging under OpenBSD Thanks to Aleksey Kozlov (avtolik@newmail.ru) for testing under OpenBSD Thanks to Roman Gorohov (idle@idle.org.ru) for the free account at OpenBSD-4.1 box * string family functions were replaced with more secure equivalents * regular expressions introduced * SECURITY section of man page contributed by Sergey Redin (sergey@redin.info) 09.01.2007 0.6.11b -------------------------------------------------------------------------------- * conversion types fixed * AIX and OSF1 code by Sylvain DEGUT merged to current source code * macros optimizations * syntax fixed at examples/em_ssh_2.sh script * Makefile update 12.2006 0.6.10-degut - version introduced by Sylvain DEGUT (sylvain.degut@neuf.fr) -------------------------------------------------------------------------------- Empty is fully operational on: AIX: 4.3, 5.1, 5.2, 5.3 HP-UX: 11.00, 11.11, 11.23 (pa-risc, itanium) Linux RedHat and Suse (32 and 64bits) OSF1: 4.0, 5.1 Solaris: 6,8,10 (sparc,x86) 15.04.2006 0.6.10a -------------------------------------------------------------------------------- * succesfully tested on HP-UX 11.11 PA-RISC1.1 (9000/777) * Empty with experimental status ported to Cygwin Linux-like environment 19.03.2006 0.6.9b -------------------------------------------------------------------------------- * Empty port of FreeBSD ports collection updated * Bugs with permissions on session-log finally fixed thanks to "Sergey Yaroshevsky" for discovering the problem * HP-UX port is ready, successfully tested on HP-UX 11.00 Thanks to Hewlett Packard AO Russia and personally to Andrew Kuchinskyi and Denis Karzhavin for C110 (9000/777) workstation * Several code optimizations * Documentation updated 18.02.2006 0.6.8b -------------------------------------------------------------------------------- * ATTENTION!!! Incompatibility changes were made!!! To control newline behavior \r and \n must be used to send CR and LF characters. * Old -n and -R options were removed * Option -p can be specified to save PID of Empty process * Bug with large PIDs in simple job control fixed * Patch for successful compilation under OpenBSD-3.8 by "Elisender" included * Session log file permissions are fixed under Solaris and Linux * Documentation updated 26.01.2006 0.6.7b - This version was never publicized -------------------------------------------------------------------------------- * With option -R Empty sends \r\n (0x0D 0x0A) instead of \n (0x0A). Thanks "Sergey V" for the idea. * Several minor bugs fixed 23.12.2005 0.6.6b -------------------------------------------------------------------------------- * Bug in IPCS on removing semaphores for the dead sessions (fixed). Thanks to "Dmitry S. Vlasov" (vlasov@quantum.ru) for discovering it. 16.12.2005 0.6.5b -------------------------------------------------------------------------------- * Option -L enables logging all input and output to the file * Bug with getopt() under Linux which was discovered by "Alex Slyotov" . Fixed * Option -r allows to read a line or a block (if -b size is specified) of data from input FIFO. Thanks to "Alex Slyotov" (aslyotov@gmail.com) for the idea. * Sucessfully tested under FreeBSD 6.0 12.11.2005 0.6.4b -------------------------------------------------------------------------------- * Empty fails (now fixed) to remove semaphores on kill-signals. The bug was discovered by Dmitry S. Vlasov vlasov@quantum.ru. Thanks! 06.11.2005 0.6.3b -------------------------------------------------------------------------------- * Empty failed to compile under Solaris-8 with gcc. Thanks to sdio (sdio4lor@gmail.com). Fixed. * Additional, lots of IPC bugs were discovered under SVR4 branch, all of them are fixed, I hope. * grantpt() under Solaris and Unixware fixed 30.10.2005 0.6.2b -------------------------------------------------------------------------------- * Option -w supports multiple pairs of "key-response" * Syntax getting harder to understand so I had removed STDIN support for -w key * Makefile reorganized: Solaris and UnixWare are moved to Makefile.SVR4 * Semaphores implemented to replase -r key timeouts. Awfull -r key was removed once and I hope forever! * Several bugs fixed * Documentation updated 25.09.2005 0.6.1b -------------------------------------------------------------------------------- * Several bugs fixed * The -k key works with job control now * Errors with creating FIFOs fixed. Thanks "Michael" (michael.auckland@gmail.com) * The -r key returned * Examples and manual page updated 18.09.2005 0.6.0b -------------------------------------------------------------------------------- * Stdin and pipes constructions can be used with -s and -w keys. * Simple job control was introduced 25.08.2005 0.5.12b -------------------------------------------------------------------------------- * Fixed non critical warnings at compilation time on FreeBSD-5.4 * SunOS-cc and SunOS-gcc targets added to Makefile to support native CC and GCC compilers. Default SunOS is equal to SunOS-gcc now. 01.05.2005 0.5.11b -------------------------------------------------------------------------------- * wait4child() bug fixed * sleep() bug removed * Timeout "-t" key changed its behavior. See man * Syslog messaging for daemon mode added * Several minor bugs fixed * Several new bugs were added :) 17.04.2005 0.5.5b -------------------------------------------------------------------------------- * Software published empty-0.6.19b/COPYRIGHT000064400256230027355000000032631202632544100175360ustar00mezakharovstorageadmins00005120000064empty - run processes under pseudo-terminal sessions Copyright (C) 2005-2012 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. empty-0.6.19b/empty.c000064400256230027355000000604041202632552500175500ustar00mezakharovstorageadmins00005120000064/* empty - run processes under pseudo-terminal sessions * * Copyright (C) 2005-2012 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 #include #if defined(__SVR4) || defined(__hpux__) #include #endif #include #include #include #ifdef __FreeBSD__ #include #endif #ifdef __OpenBSD__ #include #define EIDRM EINVAL /* Thanks "Elisender" */ #endif #if defined (__linux__) || defined (__CYGWIN__) #include #include #include #include #endif #ifdef __AIX #include #include #include #include #endif #ifdef __OSF1 #include #include #endif #ifndef __hpux__ #include #endif #if !defined(__SVR4) && !defined(__hpux__) && !defined(__AIX) && !defined(__OSF1) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define tmpdir "/tmp" #define program "empty" #define version "0.6.19b" /* -------------------------------------------------------------------------- */ 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; int fl_state = 2; /* 0 - in >>> 1 - out <<< 2 - unknown */ /* semaphores */ #ifdef _POSIX_SEMAPHORES #if defined(__linux__) && defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) /* union semun is defined by including */ #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); #ifdef __linux__ tv.tv_sec = timeout; //fix because struct was set to 0; thanks "David Hofstee" #endif if (n < 0 && errno != EINTR) perrx(255, "Fatal select()"); if (n > 0 && 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); 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"); #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"); #endif #endif /* !defined(__SVR4) && !defined(__hpux__) && !defined(__AIX) */ for (i = 1; i < 32; i++) signal(i, fsignal); /* hook signals */ 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 #if defined(__SVR4) || defined(__AIX) /* Setup terminal parameters for Solaris to work under cron or java. Thanks to "lang qiu" for the initial line of parameters. and the same for AIX 5.3 by Ralf Winkel */ tt.c_lflag = ISIG | ICANON | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; tt.c_oflag = TABDLY | OPOST; tt.c_iflag = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON | IMAXBEL; tt.c_cflag = CBAUD | CS8 | CREAD; #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) { /* our input */ (void)write(master, buf, cc); if (lfd) { if (fl_state != 1) { (void)write(lfd, ">>>", 3); fl_state = 1; } (void)write(lfd, buf, cc); } } if (FD_ISSET(master, &rfd)) if ((cc = read(master, buf, sizeof(buf))) > 0) { /* remote output */ (void)write(ofd, buf, cc); if (lfd) { if (fl_state != 0) { (void)write(lfd, "<<<", 3); fl_state = 0; } (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); #ifdef __linux__ tv.tv_sec = timeout; //fix because struct was set to 0; thanks David Hofstee" #endif if (n < 0 && errno != EINTR) perrx(255, "Fatal select()"); if (n > 0 && 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; } /* -------------------------------------------------------------------------- */ tal 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__ cfmakera