/* signature.c
*
* Copyright (C) 2000 Jonathan duSaint
*
* This file is part of Signature, a per-user daemon which implements
* a .signature file as a pipe, with the BSD program fortune on the
* other end. In this manner, a different signature is produced every
* time.
*
* Signature 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, or (at your
* option) any later version.
*
* Signature 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.
*
* The GNU General Public License is generally kept in a file called
* COPYING or LICENSE. If you do not have a copy of the license,
* write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA
* 02139, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/errno.h>
#include <signal.h>
#include <getopt.h>
#include "version.h"
/* the canonical name of the program */
#define pname "signature"
/* Defining these here makes for cleaner code. */
#define NORMAL_EXIT 0
#define ERROR_EXIT 1
/* exit_value is used so that a clean exit can be made via SIGINT. */
int exit_value = NORMAL_EXIT;
/* This is the file handle used to communicate with fortune. */
FILE *fortune_fd = NULL;
/* This is the file handle for the .signature pipe. */
int sig_fd = -1;
/* The name of the .signature file; nominally ~/.signature. */
char *sig_file;
/* SIGINT is the signal to clean up and exit. */
void
sigint (int sig)
{
/* kill fortune subprocess, if any */
if (fortune_fd != NULL) pclose (fortune_fd);
/* close pipe */
if (sig_fd < 0) close (sig_fd);
/* delete pipe */
remove (sig_file);
/* clean up memory */
free (sig_file);
/* ... and exit */
exit (exit_value);
}
/* xmalloc safely allocates memory. */
void *
xmalloc (size_t amt)
{
void *mem;
mem = malloc (amt);
if (mem == NULL)
{
fprintf (stderr, "%s: unable to allocate %d bytes of memory\n",
pname, amt);
exit (ERROR_EXIT);
}
return (mem);
}
/* Print a help message. */
void
print_help (void)
{
printf ("Signature %s:\n"
"Usage:\n"
" signature [signature_file]\n"
"\n"
"Signature creates a named pipe, nominally ~/.signature,\n"
"which, when read from, will write a signature. For the\n"
"signatures, the program fortune is used.\n",
version);
}
/* Print version and copyright information. */
void
print_version (void)
{
printf ("Signature %s:\n"
"\n"
"Copyright (C) 2000 Jonathan duSaint <jdusaint@penguinpunk.com>\n"
"\n"
"Signature comes with NO WARRANTY,\n"
"to the extent permitted by law.\n"
"You may redistribute copies of Signature\n"
"under the terms of the GNU General Public License.\n"
"For more information about these matters,\n"
"see the file named COPYING.\n",
version);
}
/* Signature, itself. */
int
main (int argc, char *argv[])
{
char line[100], *home;
int opt;
struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
{ 0, 0, 0, 0 }
};
/* parse through argv, looking for options */
while (1)
{
opt = getopt_long (argc, argv, "hv", options, NULL);
if (opt == EOF) break;
switch (opt)
{
case 'h':
print_help ();
exit (NORMAL_EXIT);
case 'v':
print_version ();
exit (NORMAL_EXIT);
case '?':
fprintf (stderr, "%s: unknown option character `%c'\n",
pname, optopt);
exit (ERROR_EXIT);
default:
fprintf (stderr,
"%s: ?? getopt returned character code 0x%x (%c) ??\n",
pname, opt, opt);
exit (ERROR_EXIT);
}
}
if (argc == 2) /* user specified file */
{
sig_file = xmalloc (strlen (argv[1]) + 1);
strcpy (sig_file, argv[1]);
}
else /* default: ~/.signature */
{
home = getenv ("HOME");
sig_file = xmalloc (strlen (home) + 12);
sprintf (sig_file, "%s/.signature", home);
}
/* install SIGINT handler */
signal (SIGINT, sigint);
/* run in background */
if (fork () != 0) return (NORMAL_EXIT);
/* create the .signature pipe */
if ((mknod (sig_file, S_IFIFO | 0666, 0) < 0)
&& (errno != EEXIST))
{
fprintf (stderr, "%s: unable to create %s\n", pname, sig_file);
exit_value = ERROR_EXIT;
kill (getpid (), SIGINT); /* exit via sigint */
}
/* loop until SIGINT is recieved */
while (1)
{
/* open the pipe for writing */
if ((sig_fd = open (sig_file, 1)) < 0)
{
fprintf (stderr, "%s: unable to open %s\n", pname, sig_file);
exit_value = ERROR_EXIT;
kill (getpid (), SIGINT); /* exit via sigint */
}
/* a read request was recieved - fire off fortune */
if ((fortune_fd = popen (FORTUNE, "r")) == NULL)
{
fprintf (stderr, "%s: unable to execute %s\n", pname, FORTUNE);
exit_value = ERROR_EXIT;
kill (getpid (), SIGINT); /* exit via sigint */
}
/* transfer fortune's output */
while ((fgets (line, 99, fortune_fd)) != NULL)
write (sig_fd, line, strlen (line));
/* clean up */
pclose (fortune_fd);
close (sig_fd);
/* and prepare for another loop */
fortune_fd = NULL;
sig_fd = -1;
}
/* not reached */
return (NORMAL_EXIT);
}