File: scanbuttond.c

package info (click to toggle)
scanbuttond 0.2.3-7
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 1,600 kB
  • ctags: 287
  • sloc: sh: 8,922; ansic: 2,263; makefile: 108
file content (383 lines) | stat: -rw-r--r-- 10,400 bytes parent folder | download
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
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
// 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.

// Modified by Milan Zamazal <pdm@debian.org> on 2008-06-06:
// Patch by Thomas Viehweger <patchesthomas.vie@web.de> -- don't segfault when
// PATH is not set.

#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'},
	{"quiet", no_argument, NULL, 'q'},
	{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;
int quiet;

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");
	printf("  -q, --quiet                 Shut up about not finding any scanners\n");
}


void process_options(int argc, char** argv)
{
	int c;

	buttonpressed_script = NULL;
	initscanner_script = NULL;
	poll_delay = -1;
	retry_delay = -1;
	daemonize = 1;
	quiet = 0;

	while ((c = getopt_long (argc, argv, "fb:s:S:p:r:hvq", 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;
			case 'q':
				quiet = 1;
				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);
	}

	// 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");
	if (oldpath == NULL) {
		oldpath = "";
	}
	char* dir = dirname(argv[0]);
	path = (char*)malloc(strlen(oldpath) + strlen(dir) + 1);
	if (path) {
		if (strlen(oldpath)) {
			strcpy(path, oldpath);
			strcat(path, ":");
		}
		else {
			path[0] = 0;
		}
		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(SIGCLD, SIG_IGN);

	syslog(LOG_INFO, "scanbuttond started");

	// main loop
	while (killed == 0) {

		if (scanners == NULL) {
			if (!quiet)
				syslog(LOG_DEBUG, "rescanning devices...");
			backend->scanbtnd_rescan();
			scanners = backend->scanbtnd_get_supported_devices();
			if (scanners == NULL) {
				if (!quiet)
					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);
}