forked from jdam6431/scanbuttond
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscanbuttond.c
368 lines (313 loc) · 9.9 KB
/
scanbuttond.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
// scanbuttond.c: the actual daemon ("frontend")
// This file is part of scanbuttond.
// Copyleft )c( 2004-2007 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 "libscanbtnd-backend_meta"
#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}
};
static char* buttonpressed_script;
static char* initscanner_script;
static char* backend_filename;
static backend_t* backend;
static long poll_delay;
static long retry_delay;
static int daemonize;
static int killed = 0;
static char* path;
char* scanbtnd_get_connection_name(int connection)
{
return connection_names[connection];
}
void shutdown(void)
{
syslog(LOG_INFO, "shutting down...");
backend->scanbtnd_exit();
scanbtnd_unload_backend(backend);
scanbtnd_loader_exit();
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 interval in microseconds, 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);
if (scanbtnd_loader_init() != 0) {
printf("Could not initialize module loader!\n");
exit(EXIT_FAILURE);
}
backend = scanbtnd_load_backend(backend_filename);
if (!backend) {
printf("Unable to load backend library \"%s\"!\n", backend_filename);
scanbtnd_loader_exit();
exit(EXIT_FAILURE);
}
// daemonize
if (daemonize) {
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);
}