1 /*
2 * beh ("Backend Error Handler") wrapper backend to extend the possibilities
3 * of handling errors of backends.
4 *
5 * Copyright 2015 by Till Kamppeter
6 *
7 * This is based on dnssd.c of CUPS
8 * dnssd.c copyright notice is follows:
9 *
10 * Copyright 2008-2015 by Apple Inc.
11 *
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "COPYING"
15 * which should have been included with this file.
16 */
17
18 /*
19 * Include necessary headers.
20 */
21
22 #include "backend-private.h"
23 #include <cups/array.h>
24 #include <ctype.h>
25
26 /*
27 * Local globals...
28 */
29
30 static int job_canceled = 0; /* Set to 1 on SIGTERM */
31
32 /*
33 * Local functions...
34 */
35
36 static int call_backend(char *uri, int argc, char **argv,
37 char *tempfile);
38 static void sigterm_handler(int sig);
39
40
41 /*
42 * 'main()' - Browse for printers.
43 */
44
45 int /* O - Exit status */
main(int argc,char * argv[])46 main(int argc, /* I - Number of command-line args */
47 char *argv[]) { /* I - Command-line arguments */
48 char *uri, *ptr, *filename;
49 char tmpfilename[1024], buf[8192];
50 int dd, att, delay, retval;
51 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
52 struct sigaction action; /* Actions for POSIX signals */
53 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
54
55 /*
56 * Don't buffer stderr, and catch SIGTERM...
57 */
58
59 setbuf(stderr, NULL);
60
61 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
62 sigset(SIGTERM, sigterm_handler);
63 #elif defined(HAVE_SIGACTION)
64 memset(&action, 0, sizeof(action));
65
66 sigemptyset(&action.sa_mask);
67 action.sa_handler = sigterm_handler;
68 sigaction(SIGTERM, &action, NULL);
69 #else
70 signal(SIGTERM, sigterm_handler);
71 #endif /* HAVE_SIGSET */
72
73 /*
74 * Check command-line...
75 */
76
77 if (argc == 1) {
78 if ((ptr = strrchr(argv[0], '/')) != NULL)
79 ptr ++;
80 else
81 ptr = argv[0];
82 printf("network %s \"Unknown\" \"Backend Error Handler\"\n",
83 ptr);
84 return (CUPS_BACKEND_OK);
85 } else if (argc < 6) {
86 fprintf(stderr,
87 "Usage: %s job-id user title copies options [file]\n",
88 argv[0]);
89 return (CUPS_BACKEND_FAILED);
90 }
91
92 /*
93 * Read out the parameters
94 */
95
96 uri = getenv("DEVICE_URI");
97 if (!uri) {
98 fprintf(stderr,
99 "ERROR: No device URI supplied!");
100 return (CUPS_BACKEND_FAILED);
101 }
102
103 ptr = strstr(uri, ":/");
104 if (!ptr) goto bad_uri;
105 ptr += 2;
106 if (*ptr == '0')
107 dd = 0;
108 else if (*ptr == '1')
109 dd = 1;
110 else
111 goto bad_uri;
112 ptr ++;
113 if (*ptr != '/') goto bad_uri;
114 ptr ++;
115 att = 0;
116 while (isdigit(*ptr)) {
117 att = att * 10 + (int)(*ptr) - 48;
118 ptr ++;
119 }
120 if (*ptr != '/') goto bad_uri;
121 ptr ++;
122 delay = 0;
123 while (isdigit(*ptr)) {
124 delay = delay * 10 + (int)(*ptr) - 48;
125 ptr ++;
126 }
127 if (*ptr != '/') goto bad_uri;
128 ptr ++;
129 fprintf(stderr,
130 "DEBUG: beh: Don't disable: %d; Attempts: %d; Delay: %d; Destination URI: %s\n",
131 dd, att, delay, ptr);
132
133 /*
134 * If reading from stdin, write everything into a temporary file
135 */
136
137 if (argc == 6) {
138 char *tmpdir;
139 int fd;
140 FILE *tmpfile;
141 size_t bytes;
142
143 tmpdir = getenv("TMPDIR");
144 if (!tmpdir)
145 tmpdir = "/tmp";
146 snprintf(tmpfilename, sizeof(tmpfilename), "%s/beh-XXXXXX", tmpdir);
147 fd = mkstemp(tmpfilename);
148 if (fd < 0) {
149 fprintf(stderr,
150 "ERROR: beh: Could not create temporary file: %s\n",
151 strerror(errno));
152 return (CUPS_BACKEND_FAILED);
153 }
154 tmpfile = fdopen(fd, "r+");
155 while ((bytes = fread(buf, 1, sizeof(buf), stdin)))
156 fwrite(buf, 1, bytes, tmpfile);
157 fclose(tmpfile);
158
159 filename = tmpfilename;
160 } else {
161 tmpfilename[0] = '\0';
162 filename = argv[6];
163 }
164
165 /*
166 * Do it!
167 */
168
169 while ((retval = call_backend(ptr, argc, argv, filename)) !=
170 CUPS_BACKEND_OK &&
171 !job_canceled) {
172 if (att > 0) {
173 att --;
174 if (att == 0)
175 break;
176 }
177 if (delay > 0)
178 sleep (delay);
179 }
180
181 if (strlen(tmpfilename) > 0)
182 unlink(tmpfilename);
183
184 /*
185 * Return the exit value of the backend only if requested
186 */
187
188 if (!dd)
189 return (retval);
190 else
191 return (CUPS_BACKEND_OK);
192
193 /*
194 * Error handling
195 */
196
197 bad_uri:
198
199 fprintf(stderr,
200 "ERROR: URI must be \"beh:/<dd>/<att>/<delay>/<original uri>\"!\n");
201 return (CUPS_BACKEND_FAILED);
202 }
203
204
205 /*
206 * 'call_backend()' - Execute the command line of the destination backend
207 */
208
209 static int
call_backend(char * uri,int argc,char ** argv,char * filename)210 call_backend(char *uri, /* I - URI of final destination */
211 int argc, /* I - Number of command line
212 arguments */
213 char **argv, /* I - Command-line arguments */
214 char *filename) { /* I - File name of input data */
215 const char *cups_serverbin; /* Location of programs */
216 char scheme[1024], /* Scheme from URI */
217 *ptr, /* Pointer into scheme */
218 cmdline[65536]; /* Backend command line */
219 int retval;
220
221 /*
222 * Build the backend command line...
223 */
224
225 strncpy(scheme, uri, sizeof(scheme) - 1);
226 if (strlen(uri) > 1023)
227 scheme[1023] = '\0';
228 if ((ptr = strchr(scheme, ':')) != NULL)
229 *ptr = '\0';
230
231 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
232 cups_serverbin = CUPS_SERVERBIN;
233
234 if (!strncasecmp(uri, "file:", 5) || uri[0] == '/') {
235 fprintf(stderr,
236 "ERROR: beh: Direct output into a file not supported.\n");
237 exit (CUPS_BACKEND_FAILED);
238 } else
239 snprintf(cmdline, sizeof(cmdline),
240 "%s/backend/%s '%s' '%s' '%s' '%s' '%s' %s",
241 cups_serverbin, scheme, argv[1], argv[2], argv[3],
242 /* Apply number of copies only if beh was called with a
243 file name and not with the print data in stdin, as
244 backends should handle copies only if they are called
245 with a file name */
246 (argc == 6 ? "1" : argv[4]),
247 argv[5], filename);
248
249 /*
250 * Overwrite the device URI and run the actual backend...
251 */
252
253 setenv("DEVICE_URI", uri, 1);
254
255 fprintf(stderr,
256 "DEBUG: beh: Executing backend command line \"%s\"...\n",
257 cmdline);
258 fprintf(stderr,
259 "DEBUG: beh: Using device URI: %s\n",
260 uri);
261
262 retval = system(cmdline) >> 8;
263
264 if (retval == -1)
265 fprintf(stderr, "ERROR: Unable to execute backend command line: %s\n",
266 strerror(errno));
267
268 return (retval);
269 }
270
271
272 /*
273 * 'sigterm_handler()' - Handle termination signals.
274 */
275
276 static void
sigterm_handler(int sig)277 sigterm_handler(int sig) { /* I - Signal number (unused) */
278 (void)sig;
279
280 fprintf(stderr,
281 "DEBUG: beh: Job canceled.\n");
282
283 if (job_canceled)
284 _exit(CUPS_BACKEND_OK);
285 else
286 job_canceled = 1;
287 }
288