// scanbuttond.c: the actual daemon ("frontend")
// This file is part of scanbuttond.
// Copyleft )c( 2004-2006 by Bernhard Stiftner
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <getopt.h>
#include "scanbuttond/config.h"
#include "scanbuttond/common.h"
#include "scanbuttond/scanbuttond.h"
#include "scanbuttond/loader.h"
#define DEF_BACKEND_FILENAME STRINGIFY(LIB_DIR) "/libscanbtnd-backend_meta.so"
#define DEF_BUTTONPRESSED_SCRIPT STRINGIFY(CFG_DIR) "/buttonpressed.sh"
#define DEF_INITSCANNER_SCRIPT STRINGIFY(CFG_DIR) "/initscanner.sh"
#define DEF_POLL_DELAY 333000L
#define MIN_POLL_DELAY 1000L
#define DEF_RETRY_DELAY 2000000L
#define MIN_RETRY_DELAY 10000L
#define BUF_SIZE 256
static char* connection_names[NUM_CONNECTIONS] =
{ "none", "libusb" };
static struct option const long_opts[] = {
{"foreground", no_argument, NULL, 'f'},
{"backend", required_argument, NULL, 'b'},
{"buttonscript", required_argument, NULL, 's'},
{"initscript", required_argument, NULL, 'S'},
{"pollingdelay", required_argument, NULL, 'p'},
{"retrydelay", required_argument, NULL, 'r'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}
};
char* buttonpressed_script;
char* initscanner_script;
char* backend_filename;
backend_t* backend;
long poll_delay;
long retry_delay;
int daemonize;
int killed = 0;
char* path;
char* scanbtnd_get_connection_name(int connection)
{
return connection_names[connection];
}
void shutdown(void)
{
syslog(LOG_INFO, "shutting down...");
backend->scanbtnd_exit();
unload_backend(backend);
syslog(LOG_DEBUG, "shutdown complete");
closelog();
}
// Ensures a graceful exit on SIGHUP/SIGTERM/SIGINT/SIGSEGV
void sighandler(int i)
{
killed = 1;
syslog(LOG_INFO, "received signal %d", i);
shutdown();
exit(i == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE);
}
// Executes an external program and wait until it terminates
void execute_and_wait(const char* program)
{
system(program);
}
void list_devices(scanner_t* devices)
{
scanner_t* dev = devices;
while (dev != NULL) {
syslog(LOG_INFO, "found scanner: vendor=\"%s\", product=\"%s\", connection=\"%s\", sane_name=\"%s\"",
dev->vendor, dev->product, scanbtnd_get_connection_name(dev->connection),
backend->scanbtnd_get_sane_device_descriptor(dev));
dev = dev->next;
}
}
void show_version(void)
{
printf("This is scanbuttond, version %s\n", VERSION);
printf("Copyleft )c( 2004-2006 by Bernhard Stiftner and contributors.\n");
printf("Scanbuttond comes with ABSOLUTELY NO WARRANTY!\n");
printf("This is free software, and you are welcome to redistribute it\n");
printf("under certain conditions; see the file COPYING for details.\n");
}
void show_usage(void)
{
printf("Usage: scanbuttond [OPTION]...\n\n");
printf("Starts a script when a button on a scanner has been pressed.\n\n");
printf("Options:\n");
printf(" -f, --foreground Run in foreground instead of background\n");
printf(" -b, --backend=FILE Use the specified backend library file\n");
printf(" default: %s\n", DEF_BACKEND_FILENAME);
printf(" -s, --buttonscript=SCRIPT The name of the script to be run when a button has been pressed\n");
printf(" default: %s\n", DEF_BUTTONPRESSED_SCRIPT);
printf(" -S, --initscript=SCRIPT The name of the script to be run to initialize the scanners\n");
printf(" default: %s\n", DEF_INITSCANNER_SCRIPT);
printf(" -p, --pollingdelay=DELAY The polling delay (ms), default: %ld\n", DEF_POLL_DELAY);
printf(" -r, --retrydelay=DELAY The retry delay (ms), default: %ld\n", DEF_RETRY_DELAY);
printf(" -h, --help Shows this screen\n");
printf(" -v, --version Shows the version\n");
}
void process_options(int argc, char** argv)
{
int c;
buttonpressed_script = NULL;
initscanner_script = NULL;
poll_delay = -1;
retry_delay = -1;
daemonize = 1;
while ((c = getopt_long (argc, argv, "fb:s:S:p:r:hv", long_opts, NULL)) != -1) {
switch (c) {
case 'f':
daemonize = 0;
break;
case 'b':
backend_filename = optarg;
break;
case 's':
buttonpressed_script = optarg;
break;
case 'S':
initscanner_script = optarg;
break;
case 'p':
poll_delay = atol(optarg);
if (poll_delay < MIN_POLL_DELAY) {
printf("Invalid polling delay (%ld). Must be at least %ld.\n",
poll_delay, MIN_POLL_DELAY);
exit(EXIT_FAILURE);
}
break;
case 'r':
retry_delay = atol(optarg);
if (retry_delay < MIN_RETRY_DELAY) {
printf("Invalid retry delay (%ld). Must be at least %ld.\n",
retry_delay, MIN_RETRY_DELAY);
exit(EXIT_FAILURE);
}
break;
case 'h':
show_usage();
exit(EXIT_SUCCESS);
break;
case 'v':
show_version();
exit(EXIT_SUCCESS);
break;
}
}
if (backend_filename == NULL)
backend_filename = DEF_BACKEND_FILENAME;
if (buttonpressed_script == NULL)
buttonpressed_script = DEF_BUTTONPRESSED_SCRIPT;
if (initscanner_script == NULL)
initscanner_script = DEF_INITSCANNER_SCRIPT;
if (poll_delay == -1)
poll_delay = DEF_POLL_DELAY;
if (retry_delay == -1)
retry_delay = DEF_RETRY_DELAY;
}
int main(int argc, char** argv)
{
int button;
int result;
pid_t pid, sid;
scanner_t* scanners;
scanner_t* scanner;
process_options(argc, argv);
backend = load_backend(backend_filename);
if (!backend) {
printf("Unable to load backend library \"%s\"!\n", backend_filename);
exit(EXIT_FAILURE);
}
openlog(NULL, 0, LOG_DAEMON);
// daemonize
if (daemonize) {
if (daemon(0, 0) != 0) {
syslog(LOG_ERR, "daemon() failed.");
}
}
/*
pid = fork();
if (pid < 0) {
printf("Can't fork!\n");
exit(EXIT_FAILURE);
} else if (pid > 0) {
exit(EXIT_SUCCESS);
}
}
umask(0);
openlog(NULL, 0, LOG_DAEMON);
// create a new session for the child process
if (daemonize) {
sid = setsid();
if (sid < 0) {
syslog(LOG_ERR, "Could not create a new SID! Terminating.");
exit(EXIT_FAILURE);
}
}
// Change the current working directory
if ((chdir("/")) < 0) {
syslog(LOG_WARNING, "Could not chdir to /. Hmmm, strange... "\
"Trying to continue.");
}
// close standard file descriptors
if (daemonize) {
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
*/
// setup the environment
char* oldpath = getenv("PATH");
char* dir = dirname(argv[0]);
path = (char*)malloc(strlen(oldpath) + strlen(dir) + 1);
strcpy(path, oldpath);
strcat(path, ":");
strcat(path, dir);
setenv("PATH", path, 1);
free(path);
syslog(LOG_DEBUG, "running scanner initialization script...");
execute_and_wait(initscanner_script);
syslog(LOG_DEBUG, "initialization script executed.");
if (backend->scanbtnd_init() != 0) {
syslog(LOG_ERR, "Error initializing backend. Terminating.");
exit(EXIT_FAILURE);
}
scanners = backend->scanbtnd_get_supported_devices();
if (scanners == NULL) {
syslog(LOG_WARNING, "no known scanner found yet, " \
"waiting for device to be attached");
}
list_devices(scanners);
signal(SIGTERM, &sighandler);
signal(SIGHUP, &sighandler);
signal(SIGINT, &sighandler);
signal(SIGSEGV, &sighandler);
signal(SIGCHLD, SIG_IGN);
syslog(LOG_INFO, "scanbuttond started");
// main loop
while (killed == 0) {
if (scanners == NULL) {
syslog(LOG_DEBUG, "rescanning devices...");
backend->scanbtnd_rescan();
scanners = backend->scanbtnd_get_supported_devices();
if (scanners == NULL) {
syslog(LOG_DEBUG, "no supported devices found. rescanning in a few seconds...");
usleep(retry_delay);
continue;
}
syslog(LOG_DEBUG, "found supported devices. running scanner initialization script...");
execute_and_wait(initscanner_script);
syslog(LOG_DEBUG, "initialization script executed.");
scanners = backend->scanbtnd_get_supported_devices();
continue;
}
scanner = scanners;
while (scanner != NULL) {
result = backend->scanbtnd_open(scanner);
if (result != 0) {
syslog(LOG_WARNING, "scanbtnd_open failed, error code: %d", result);
if (result == -ENODEV) {
// device has been disconnected, force re-scan
syslog(LOG_INFO, "scanbtnd_open returned -ENODEV, device rescan will be performed");
scanners = NULL;
usleep(retry_delay);
break;
}
usleep(retry_delay);
break;
}
button = backend->scanbtnd_get_button(scanner);
backend->scanbtnd_close(scanner);
if ((button > 0) && (button != scanner->lastbutton)) {
syslog(LOG_INFO, "button %d has been pressed.", button);
scanner->lastbutton = button;
char cmd[BUF_SIZE];
snprintf(cmd, BUF_SIZE, "%s %d %s", buttonpressed_script, button,
backend->scanbtnd_get_sane_device_descriptor(scanner));
execute_and_wait(cmd);
}
if ((button == 0) && (scanner->lastbutton > 0)) {
syslog(LOG_INFO, "button %d has been released.", scanner->lastbutton);
scanner->lastbutton = button;
}
scanner = scanner->next;
}
usleep(poll_delay);
}
syslog(LOG_WARNING, "exited main loop!?!");
shutdown();
exit(EXIT_SUCCESS);
}
syntax highlighted by Code2HTML, v. 0.9.1