/*
spinner.c
vers 1.2.3
$Revision: 1.9 $
Copyright (C) 2002, 2003 Joe Laffey <software@-REMOVE-THIS-laffeycomputer.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* This program makes a little character spinner. */
/* e.g. - \ | / - \ | / ... */
/* It is useful for keeping links alive, etc. */
/* http://www.laffeycomputer.com/software.html */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <sysexits.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <pwd.h>
#include <termios.h>
#include <setjmp.h>
#include "spinner.h"
#include "usage.h"
/* These are the chars used to make the spinner */
/* You may edit these, changing the count if desired */
#define SPINNER_CHARS { '-', '\\', '|','/','-','\\','|','/' }
/* Maximum value for the time argument passed */
/* This is pretty insignificant. */
#define MAX_TIME 65000
/****************/
/* Static vars */
/* NUmber of times our interrupt signal handler is called */
static int gInterrupted = 0;
/* jump buffer for return from our read timeout signal handler */
static sigjmp_buf gAlarmJump;
/* Flag to decide if we can call siglongjmp yet */
static volatile sig_atomic_t gCanJump = 0;
/******************************/
/* Handle interrupt Signals */
static void DoInterrupt(void)
{
int saveErrno;
gInterrupted++;
/* for a quit if we get two signals in a row before we have quit normally */
if(gInterrupted > 1)
{
_exit(EX_TEMPFAIL);
}
}
/***************************************************************/
/* Signal Alarm handler to interrupt blocked open of a TTY */
static void InterruptBlockedOpen(void)
{
if(gCanJump == 0 )
return; /* Not ready for signal yet */
/* Make sure we don't do this again */
gCanJump = 0;
/* just jump back to our setjump */
siglongjmp( gAlarmJump, 1);
}
/****************************/
/* Become a daemon proc */
static int Daemonize(int verbose, char* logFileName, int logFileSet, FILE* logfile, int doPidFile, char* pidFileName)
{
pid_t pid;
if( (pid = fork()) < 0 )
return (-1);
else if( pid != 0 )
{
if(doPidFile)
{
WritePidFile(verbose, pid, logfile, pidFileName);
}
/* poorly assume %d is good for a pid */
if(verbose)
{
if(logFileSet)
{
fprintf(logfile, "%s: Parent process terminating.\n", PACKAGE);
fprintf(logfile, "%s: Child process ( pid %d ) continuing...\n", PACKAGE, pid);
fprintf(logfile, "%s: Further output to %s\n", PACKAGE, logFileName);
}
}
exit(0); /*parent exits stage left */
}
/* now child */
setsid(); /* become sess leader */
chdir("/"); /* chdir to root to allow unmounting of filesystems, etc. */
umask(0);
/* Close all of these up */
/* We must close all of them, including stderr to allow logout on linux. */
/* Any descriptors open on the active TTY prevent a logout on linux, BSD is OK. */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
return(0);
}
/****************************/
/* Create our pid file */
static void WritePidFile(int verbose, pid_t pid, FILE* logfile, char* pidFileName )
{
FILE* pidFile;
int pidFileDes;
if( 0 > (pidFileDes = open(pidFileName, O_CREAT | O_WRONLY | O_TRUNC, 0600)) )
{
fprintf(logfile, "%s: WARNING: Unable to open pid file %s for writing.\n%s: %s\n%s: WARNING: No pid file will be written.\n", PACKAGE, pidFileName, PACKAGE, strerror(errno), PACKAGE);
return;
}
pidFile = fdopen(pidFileDes, "w");
if(pidFile == NULL)
{
fprintf(logfile, "%s: WARNING: Unable to fdopen pid file %s for writing.\n%s: %s\n", PACKAGE, pidFileName, PACKAGE, strerror(errno));
exit(1);
}
else
{
if(verbose)
fprintf(logfile, "%s: Writing daemon's pid to pid file %s.\n", PACKAGE, pidFileName);
/* poorly assume %d is good for a pid */
fprintf(pidFile, "%d\n", pid );
fclose(pidFile);
}
}
/*********************/
/* Open the TTY */
static inline int OpenTTY(int logFileSet, FILE* logfile, char* whichTTY, char* ttyName )
{
int ttyFileDes;
/* This sometimes blocks on tty's not currently in use, depending on OS, but I am hesitant to try to open a tty nonblock and proceed as usual */
/* So we use an alaram */
if( SIG_ERR == signal(SIGALRM, (void *)InterruptBlockedOpen) )
{
if(logFileSet)
fprintf(logfile, "%s: Unable to install SIGALRM signal handler to interrupt blocks to open()!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
/* Use sigsetjmp and siglongjmp to interrupt blocking I/O. */
/* This method is used to prevent a race condition betweenn the initial call to alarm() */
/* and the blocking system call. It also handles restarted system calls. See Stevens. */
/* Here we set the "return" address for our future call to siglongjump */
if( 0 != sigsetjmp(gAlarmJump, 1) )
{
/* if sigsetjump returns nonzero then we have arrived here because of a call to siglongjump() */
/* That call could only have come from our singnal handler InterruptBlockedOpen() */
/* So we abort with a message about the timeout. */
if(logFileSet)
fprintf(logfile, "%s: The attempt to open the TTY %s\n%s: for writing timed out after %d seconds.\n%s: Perhaps that is not an active TTY...\n", PACKAGE, whichTTY, PACKAGE, TTY_OPEN_TIMEOUT, PACKAGE);
exit(1);
}
/* OK to to use siglongjmp */
gCanJump = 1;
/* Set the alarm to interrupt a blocking open of the TTY */
alarm( TTY_OPEN_TIMEOUT );
/* Open the TTY */
ttyFileDes = open( whichTTY, O_WRONLY );
if(ttyFileDes < 0)
{
if(logFileSet)
fprintf(logfile, "%s: Unable to open %s for writing!\n%s: %s\n", PACKAGE, whichTTY, PACKAGE, strerror(errno));
exit(1);
}
/* turn off the interrupt alarm */
alarm(0);
/* Remove the alarm handler */
if( SIG_ERR == signal(SIGALRM, SIG_DFL) )
{
if(logFileSet)
fprintf(logfile, "%s: Unable to set SIGALRM signal handler to default!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
/* Test to see if we opened a terminal */
if(!isatty(ttyFileDes))
{
if(logFileSet)
fprintf(logfile, "%s: %s is not an active terminal!\n", PACKAGE, whichTTY);
exit(1);
}
/* get the name of our term */
strncpy(ttyName, ttyname(ttyFileDes), _POSIX_PATH_MAX);
ttyName[_POSIX_PATH_MAX] = '\0';
return ttyFileDes;
}
/***********************************/
/* Send Reset code to the term */
static void ResetTerm( int verbose, int logFileSet, FILE* logfile, char* whichTTY, char* ttyName )
{
int ttyDes;
if(verbose)
{
if(logFileSet)
fprintf(logfile, "%s: Attempting to reset terminal...\n", PACKAGE);
sleep(1);
}
/* Open up the TTY and get its name */
ttyDes = OpenTTY( logFileSet, logfile, whichTTY, ttyName );
write(ttyDes, VT100_RESET_CODE, sizeof(VT100_RESET_CODE)); /* call reset term */
if( 0 != close(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to close TTY %s!\n", PACKAGE, ttyName);
_exit(1);
}
if(verbose)
{
if(logFileSet)
fprintf(logfile, "%s: Terminal reset code written.\n", PACKAGE);
}
return;
}
/***************************************************/
/* retrieve the default filename. Use /etc/passwd */
static int GetHomePath(int verbose, FILE* logfile, char* fullHomePath)
{
/* the HOME string */
char* homePath;
struct passwd *pwdPtr;
/* try to get home out of passwd db*/
if( NULL == (pwdPtr = getpwuid(getuid()) ) )
{
fprintf(logfile, "%s: WARNING: Unable to determine HOME directory.\n%s: WARNING: No pid file will be written.\n", PACKAGE, PACKAGE);
}
else
{
/* this ought never be null, but code safely */
if( pwdPtr->pw_dir == NULL )
{
fprintf(logfile, "%s: Home dir entry (pw_dir) from passwd database was null. Aborting.\n", PACKAGE);
return(1);
}
else
{
int len;
homePath = malloc(MAX(strlen(pwdPtr->pw_dir)+1, _POSIX_PATH_MAX+1) );
if(homePath == NULL)
{
fprintf(logfile, "%s: Unable to allocate memory for home directory string!\n", PACKAGE);
return(1);
}
strncpy(homePath, pwdPtr->pw_dir, _POSIX_PATH_MAX);
homePath[_POSIX_PATH_MAX] = '\0'; /* to be safe */
if(verbose)
fprintf(logfile, "%s: Retrieved HOME directory from passwd database.\n", PACKAGE);
strncpy(fullHomePath, homePath , _POSIX_PATH_MAX);
free(homePath);
len = strlen(fullHomePath);
if( fullHomePath[len] != PATH_SEPARATOR )
{
if(len < _POSIX_PATH_MAX)
{
/* add a slash */
fullHomePath[len] = PATH_SEPARATOR;
fullHomePath[len + 1] = '\0'; /* Terminate ( see Arnold, et. al. ) */
}
else
{
fprintf(logfile, "%s: Length of retrieved home directory is too long!!\n", PACKAGE);
return(1);
}
}
}
} /* end else of if( pwdPtr->pw_dir == NULL ) */
/* fullHomePath is modified, as it is called by reference */
return(0);
}
/****************************************/
/* MainLoop for Printing the spinner */
static int SpinnerLoop( int verbose, int inverse, int time, int microTime, int logFileSet, FILE* logfile, char* whichTTY, char* ttyName, char* failMsg )
{
/* the file descriptor of the terminal to which we write */
int ttyDes;
/* The spinning characters */
char chars[] = SPINNER_CHARS;
/* length of above */
int numChars = sizeof(chars);
/* holds the pre-built strings of code to display */
char cache[sizeof(chars)][OUTPUT_STR_MAX + 1];
/* length of the cached strings. They are all the same */
int strLen = 0; /* defaulf to quell warnings*/
/* count.. */
unsigned int i = 0; /* defaulf to quell warnings*/
/* signal sets */
sigset_t sigFilledSet;
sigset_t sigSavedSet;
int exitCode = 0;
if(verbose)
{
if(logFileSet)
fprintf(logfile, "%s: Generating display strings for spinner.\n", PACKAGE);
}
/*We pre-cache all the character strings for the spinner */
for(i = 0; i < numChars; i++)
{
/* These sprintfs create the strings that get sent to the term. */
/* They include vt100 escape codes. */
sprintf(
cache[i], "%s%s%s%c%s",
VT100_STORE_POS_AND_SETTINGS_CODE,
VT100_HOME_CODE,
(inverse)?VT100_INVERSE_CODE:"",
chars[i],
VT100_RESTORE_POS_AND_SETTINGS_CODE
);
}
/* length of the cacched strings.. They are all the same length */
strLen = strlen(cache[0]);
if(verbose)
{
if(logFileSet)
fprintf(logfile, "%s: Setting up signal handlers...\n", PACKAGE);
}
/* Set up our signal handlers... */
if( SIG_ERR == signal(SIGINT, (void *)DoInterrupt))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to install SIGINT signal handler!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
if( SIG_ERR == signal(SIGTERM, (void *)DoInterrupt))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to install SIGTERM signal handler!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
/* We really ought not be receiving this, since we go daemon... */
if( SIG_ERR == signal(SIGHUP, (void *)DoInterrupt))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to install SIGHUP signal handler!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
/* Set up our signal masks */
if( 0 != sigfillset( &sigFilledSet ))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to fill in signal mask!\n", PACKAGE);
_exit(1);
}
if(verbose)
{
if(logFileSet)
fprintf(logfile, "%s: Commencing loop to display spinner...\n", PACKAGE);
}
/* main loop */
while(!gInterrupted)
{
if( i < MAXINT )
i++;
else
i = 0;
/* The TTY is opened and closed each time through the loop because sshd on linux */
/* will not logout with the tty open. BSD works fine... */
/* Open up the TTY and get its name */
ttyDes = OpenTTY( logFileSet, logfile, whichTTY, ttyName );
/* Block all signals we can during write so we don't end up with the cursor */
/* in a bad place, or mode (like inverse) */
if( 0 > sigprocmask( SIG_BLOCK, &sigFilledSet, &sigSavedSet ))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to set process signal mask!\n", PACKAGE);
_exit(1);
}
/* Be sure we still have an active term */
if(!isatty(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: %s is no longer an active terminal!\n", PACKAGE, ttyName);
/* Bail out */
_exit(1);
}
/* Drain the terminal driver output queue */
/* to be sure we can send our codes uninterrupted */
if( -1 == tcdrain(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to drain terminal output!\n", PACKAGE);
_exit(1);
}
/* Print our line */
if( -1 == write(ttyDes, cache[i % numChars], strLen) )
{
/* If this failed due to a temporarily unavailable code (EAGAIN) then ignore it. */
/* Mac OSX generates this a lot. It seems to be Terminal.app as it works OK in XWindows on OS X */
if(errno == EAGAIN)
{
/* Ignore */
}
else
{
snprintf(failMsg, MAX_FAIL_MSG_LEN, "%s: write failed!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
failMsg[MAX_FAIL_MSG_LEN] = '\0';
gInterrupted = 1; /* we're gonna quit, but got to clenup */
exitCode = 1;
}
}
/* Drain the terminal driver output queue */
/* to be sure what we sent is uninterrupted */
if( -1 == tcdrain(ttyDes))
{
fprintf(logfile, "%s: Unable to drain terminal output!\n", PACKAGE);
_exit(1);
}
/* restore the signals */
if( 0 > sigprocmask( SIG_SETMASK, &sigSavedSet, NULL ))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to set process signal mask!\n", PACKAGE);
_exit(1);
}
if( 0 != close(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to close TTY %s!\n", PACKAGE, ttyName);
_exit(1);
}
if(microTime)
usleep(time);
else
sleep(time);
} /* end main loop */
return exitCode;
}
/****************************************/
/* MainLoop for Printing the nulls */
static int NullLoop( int verbose, int time, int microTime, int logFileSet, FILE* logfile, char* whichTTY, char* ttyName, char* failMsg )
{
char theNull[] = {'\0'};
int exitCode = 0;
int ttyDes;
if(verbose)
{
if(logFileSet)
fprintf(logfile, "%s: Setting up signal handlers...\n", PACKAGE);
}
/* Set up our signal handlers... */
if( SIG_ERR == signal(SIGINT, (void *)DoInterrupt))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to install SIGINT signal handler!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
if( SIG_ERR == signal(SIGTERM, (void *)DoInterrupt))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to install SIGTERM signal handler!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
/* We really ought not be receiving this, since we go daemon... */
if( SIG_ERR == signal(SIGHUP, (void *)DoInterrupt))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to install SIGHUP signal handler!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
exit(1);
}
if(verbose)
{
if(logFileSet)
fprintf(logfile, "%s: Commencing loop to send nulls...\n", PACKAGE);
}
while(!gInterrupted)
{
/* The TTY is opened and closed each time through the loop because sshd on linux */
/* will not logout with the tty open. BSD works fine... */
/* Open up the TTY and get its name */
ttyDes = OpenTTY( logFileSet, logfile, whichTTY, ttyName );
/* Be sure we still have an active term */
if(!isatty(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: %s is no longer an active terminal!\n", PACKAGE, ttyName);
/* Bail out */
_exit(1);
}
/* Drain the terminal driver output queue */
/* to be sure what we sent is uninterrupted */
/* This is probably not needed in null mode, but it just seems like a good idea.. */
if( -1 == tcdrain(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to drain terminal output!\n", PACKAGE);
_exit(1);
}
if( -1 == write(ttyDes, theNull, sizeof(theNull)) )
{
/* If this failed due to a temporarily unavailable code (EAGAIN) then ignore it. */
/* Mac OSX generates this a lot. It seems to be Terminal.app as it works OK in XWindows on OS X */
if(errno == EAGAIN)
{
/* Ignore */
}
else
{
snprintf(failMsg, MAX_FAIL_MSG_LEN, "%s: write failed!\n%s: %s\n", PACKAGE, PACKAGE, strerror(errno));
failMsg[MAX_FAIL_MSG_LEN] = '\0';
gInterrupted = 1; /* we're gonna quit, but got to clenup */
exitCode = 1;
}
}
/* Drain the terminal driver output queue */
/* to be sure what we sent is uninterrupted */
/* This is probably not needed in null mode, but it just seems like a good idea.. */
if( -1 == tcdrain(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to drain terminal output!\n", PACKAGE);
_exit(1);
}
if( 0 != close(ttyDes))
{
if(logFileSet)
fprintf(logfile, "%s: Unable to close TTY %s!\n", PACKAGE, ttyName);
_exit(1);
}
if(microTime)
usleep(time);
else
sleep(time);
}
return exitCode;
}
/****************************/
/* Main Entry */
int main(int argc, char** argv)
{
/* thechar array name of the terminal to which we write */
char ttyName[_POSIX_PATH_MAX + 1];
/* Should we send only nulls? */
int sendNulls = 0;
/* time of sleep in secs or microseconds depending on switch*/
int time;
/* incoming name of tty to open from -t option */
char whichTTY[_POSIX_PATH_MAX + 1];
/* for getopt */
int ch;
/* should we use microtime for delay ? */
int microTime = 0;
/* Should we inverse the spinner ? */
int inverse = 1;
/* should we open another TTY other than DEFAULT_TTY ?*/
int useTTY = 0;
/* print extra garbage */
int verbose = 0;
/* Should we set the priority? defaults to a lower priority */
int setPriority = 1;
/* The default value to use for priority */
int priority = DEFAULT_PRIORITY;
/* Set the envrionment var to contain the child PID ? */
int doPidFile = 1;
/* hold the file path to the pid file */
char pidFileName[_POSIX_PATH_MAX + 1] = "";
/* hold the file path to the log file */
char logFileName[_POSIX_PATH_MAX + 1] = "";
/* hold the file path to the user's home dir */
char fullHomePath[_POSIX_PATH_MAX + 1] = "";
/* did the user specify a pid file ? */
int pidFileSet = 0;
/* did the user specify a log file ? */
int logFileSet = 0;
/* hold a failure message for later display after reset */
char failMsg[MAX_FAIL_MSG_LEN +1] = "";
/* the exit code to return */
int exitCode = 0;
/* Should we try to reset the term on quit ? */
int resetOnQuit = 0;
/* Should we try to reset the term ONLY and then quit (do nothing else)? */
int resetOnly = 0;
/* Should we check the TERM env var to be vt100 or vt102 ? */
int checkTermType = 1;
/* the TERM string from the env */
char* termType;
/* file to write errors to. This starts as stderr, and switches to a logfile on daemonizing */
FILE* logfile;
while ((ch = getopt(argc, argv, "Iut:vp:Pf:l:FRTLn")) != -1) /* get our options */
switch(ch) {
case 'I':
inverse = 0;
break;
case 'u':
/* delay should be in microseconds as opposed to default of seconds */
microTime = 1;
break;
case 't':
useTTY = 1;
strncpy(whichTTY, optarg, _POSIX_PATH_MAX);
whichTTY[_POSIX_PATH_MAX] = '\0'; /* be safe */
break;
case 'v':
verbose = 1;
break;
case 'P':
setPriority = 0;
break;
case 'n':
sendNulls = 1;
/* no need for this to send nulls */
checkTermType = 0;
break;
case 'p':
priority = atoi(optarg);;
break;
case 'F':
doPidFile = 0;
break;
case 'f':
/* pid file path */
pidFileSet = 1;
strncpy(pidFileName, optarg, _POSIX_PATH_MAX);
pidFileName[_POSIX_PATH_MAX] = '\0'; /* SafteyPup sez, "terminate your strings" */
break;
case 'l':
/* log file path */
logFileSet = 1;
strncpy(logFileName, optarg, _POSIX_PATH_MAX);
logFileName[_POSIX_PATH_MAX] = '\0'; /*terminate */
break;
case 'R':
/* reset only*/
resetOnly = 1;
doPidFile = 0;
break;
case 'r':
resetOnQuit = 1;
break;
case 'T':
checkTermType = 0;
break;
case 'L':
License();
break;
default:
usage();
}
argc -= optind;
argv += optind;
if(argc > 2)
usage();
/* Default to writing errors to stderr */
logfile = stderr;
/* Stop any silly admin that make this setuid... */
if(getuid() != geteuid())
{
fprintf(logfile, "%s: **** This program is setuid! ****\n%s: **** Terminating due to security concerns! ****\n%s: If you *must* you could use setgid, but this is not recommened either.\n", PACKAGE, PACKAGE, PACKAGE);
exit(1);
}
/* Warn admins that make this setgid. And refuse to operate on files. */
if(getgid() != getegid())
{
/* here we poorly assume %d is right for a gid */
fprintf(logfile, "%s: WARNING: *** This program is setgid to gid %d ! ****\n%s: WARNING: Pid file has been disabled for security reasons.\n", PACKAGE, getegid(), PACKAGE);
/* disable for security against symlink attacks, etc. */
doPidFile = 0;
}
/* Check term type */
if(checkTermType)
{
if(NULL == (termType = getenv( "TERM" ) ))
{
fprintf(logfile, "%s: Unable to determine TERM type from the environment.\n%s: Use -T switch to override check and use VT100 codes anyway.\n", PACKAGE, PACKAGE);
exit(1);
}
if( ! (
(0 == strncasecmp(termType, "VT100", sizeof("VT100") ))
|| (0 == strncasecmp(termType, "VT102", sizeof("VT102") ))
|| (0 == strncasecmp(termType, "XTERM", sizeof("XTERM") ))
|| (0 == strncasecmp(termType, "SCREEN", sizeof("SCREEN") ))
|| (0 == strncasecmp(termType, "ANSI", sizeof("ANSI") ))
)
)
{
fprintf(logfile, "%s: I cannot determine if your TERM type from the environment (%s)\n%s: is VT100 compatible.\n%s: Use -T switch to override this check and use VT100 codes anyway.\n%s: In most cases this is fine (unless you have a \"dumb\" terminal).\n", PACKAGE, termType, PACKAGE, PACKAGE, PACKAGE);
exit(1);
}
}
if(doPidFile && !pidFileSet)
{
/* retrieve the home directory for the cur user */
if(0 != GetHomePath(verbose, logfile, fullHomePath))
{
fprintf(logfile, "%s: Unable to retrieve path to HOME directory from /etc/passwd!.\n", PACKAGE);
exit(1);
}
strncpy(pidFileName, fullHomePath, _POSIX_PATH_MAX );
strncat(pidFileName, DEFAULT_PID_FILENAME, (_POSIX_PATH_MAX - strlen(pidFileName) - 1) );
pidFileName[_POSIX_PATH_MAX] = '\0'; /* terminate this bad boy */
}
/* Print some info out for the user if they want it */
if(verbose)
{
fprintf(logfile, "%s: Inverse mode %s.\n", PACKAGE, (inverse? "enabled":"disabled"));
if(useTTY)
fprintf(logfile, "%s: Using user-selected TTY: %s.\n", PACKAGE, whichTTY);
if(resetOnQuit)
fprintf(logfile, "%s: Will attempt to reset the terminal on quit.\n", PACKAGE);
else
fprintf(logfile, "%s: Will not attempt to reset the terminal on quit.\n", PACKAGE);
if(setPriority)
fprintf(logfile, "%s: Will attempt to set process priority to %d.\n", PACKAGE, priority);
else
fprintf(logfile, "%s: Will not alter process priority.\n", PACKAGE);
}
if(argc < 1)
{
/* We did not get a delay specified */
time = DEFAULT_DELAY;
/* Don't use microTime with the default, as this could be a delay like 2 usec (insane) */
microTime = 0;
if(verbose)
fprintf(logfile, "%s: No delay specified. Using default delay.\n", PACKAGE);
}
else
{
/* nab the delay from arg */
time = atoi(argv[0]);
if(time < 0 || time > MAX_TIME)
{
fprintf(logfile, "%s: Delay values must be between 0 and %d inclusive\n", PACKAGE, MAX_TIME);
exit(1);
}
}
if(verbose)
fprintf(logfile, "%s: Using %d %ssec delay.\n", PACKAGE, time, (microTime?"u":""));
/* If some admin made this setgid we now drop privs */
/* The only reason anyone would do this is to open the tty as group tty */
if(getgid() != getegid())
{
if( 0 != setgid( getgid() ) )
{
fprintf(logfile, "%s: Unable to drop group priviledges!\n%s,: Aborting...", PACKAGE, PACKAGE);
exit(1);
}
}
if(!useTTY)
{
/* use our input tty. We cannot use /dev/tty here because when we */
/* become a daemon below it is no longer valid!! */
strncpy(whichTTY, ttyname(STDIN_FILENO), _POSIX_PATH_MAX);
whichTTY[_POSIX_PATH_MAX] = '\0';
}
if( resetOnly)
{
/* All we do is reset the term */
ResetTerm(verbose, 1, logfile, whichTTY, ttyName);
exit(0);
}
/* Set up our priority if required */
if(setPriority)
{
if(verbose)
fprintf(logfile, "%s: Setting process priority to %d.\n", PACKAGE, priority);
if( -1 == setpriority( PRIO_PROCESS, 0 /* our process */, priority ) )
{
fprintf(logfile, "%s: WARNING: Unable to set process priority to %d.\n%s: WARNING: %s\n", PACKAGE, priority, PACKAGE, strerror(errno));
}
}
/* become a daemon - muahahahaha! */
if(verbose)
fprintf(logfile, "%s: Launching into the background. (Daemonizing...)\n", PACKAGE);
/* Open up the logfile - BEFORE we become a daemon so we still have someplace to send an error msg */
if(logFileSet)
{
logfile = fopen(logFileName, "a");
if( logfile == NULL )
{
fprintf(stderr, "%s: Unable to open logfile for appending! Location: %s\n", PACKAGE, logFileName);
exit(1);
}
/* linebuffer the logfile - Stream is still function if this fails, so no error check...*/
setvbuf( logfile, NULL, _IOLBF, 0);
}
/* Note we pass stderr as the logfile name because we still want output to stderr at this point */
if( Daemonize( verbose, logFileName, logFileSet, stderr, doPidFile, pidFileName ) != 0)
{
fprintf(stderr, "%s: Unable to become a daemon!\n", PACKAGE);
exit(1);
}
if(verbose)
{
if(logFileSet)
fprintf(logfile, "\n%s: Begin output to logfile.\n", PACKAGE);
}
/* Enter our main loop depending on whether we are sending */
/* the spinner characters or nulls. */
if(sendNulls)
{
exitCode = NullLoop(verbose, time, microTime, logFileSet, logfile, whichTTY, ttyName, failMsg);
}
else
{
exitCode = SpinnerLoop(verbose, inverse, time, microTime, logFileSet, logfile, whichTTY, ttyName, failMsg);
}
/* clean up pid file */
if(doPidFile)
{
if( 0 != (unlink( pidFileName )) )
{
if(logFileSet)
fprintf(logfile, "%s: WARNING: Unable to delete pid file %s.\n%s: WARNING: %s\n", PACKAGE, pidFileName, PACKAGE, strerror(errno));
}
}
/* reset term if possible */
/* This is an attempt to keep the terminal in order when spinner stops. */
/* Nevertheless, on some systems, we end up with the cursor in the wrong */
/* place, or in thw wrong mode, when we are stopped mid-write. */
/* FIXED: I was not blocking the signals correctly. */
/* This should almost never be needed now, but it here for historical reasons. */
if( resetOnQuit)
{
ResetTerm(verbose, logFileSet, logfile, whichTTY, ttyName);
}
/* print fail message if needed - AFTER we reset the term...*/
if(logFileSet)
{
fprintf(logfile, "%s", failMsg );
if(verbose)
fprintf(logfile, "%s: Exiting normally..\n", PACKAGE);
fclose(logfile);
}
return(exitCode);
}
syntax highlighted by Code2HTML, v. 0.9.1