• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <sys/wait.h>
26 
27 /*
28  * Local globals...
29  */
30 
31 static volatile int job_canceled = 0; /* Set to 1 on SIGTERM */
32 
33 /*
34  * Local functions...
35  */
36 
37 static int		call_backend(char *uri, int argc, char **argv,
38 				     char *tempfile);
39 static void		sigterm_handler(int sig);
40 
41 
42 /*
43  * 'main()' - Browse for printers.
44  */
45 
46 int					/* O - Exit status */
main(int argc,char * argv[])47 main(int  argc,				/* I - Number of command-line args */
48      char *argv[]) {			/* I - Command-line arguments */
49   char *uri, *ptr, *filename;
50   char tmpfilename[1024], buf[8192];
51   int dd, att, delay, retval;
52 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
53   struct sigaction action;		/* Actions for POSIX signals */
54 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
55 
56  /*
57   * Don't buffer stderr, and catch SIGTERM...
58   */
59 
60   setbuf(stderr, NULL);
61 
62 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
63   sigset(SIGTERM, sigterm_handler);
64 #elif defined(HAVE_SIGACTION)
65   memset(&action, 0, sizeof(action));
66 
67   sigemptyset(&action.sa_mask);
68   action.sa_handler = sigterm_handler;
69   sigaction(SIGTERM, &action, NULL);
70 #else
71   signal(SIGTERM, sigterm_handler);
72 #endif /* HAVE_SIGSET */
73 
74  /*
75   * Check command-line...
76   */
77 
78   if (argc == 1) {
79     if ((ptr = strrchr(argv[0], '/')) != NULL)
80       ptr ++;
81     else
82       ptr = argv[0];
83     printf("network %s \"Unknown\" \"Backend Error Handler\"\n",
84 	   ptr);
85     return (CUPS_BACKEND_OK);
86   } else if (argc < 6) {
87     fprintf(stderr,
88 	    "Usage: %s job-id user title copies options [file]\n",
89 	    argv[0]);
90     return (CUPS_BACKEND_FAILED);
91   }
92 
93  /*
94   * Read out the parameters
95   */
96 
97   uri = getenv("DEVICE_URI");
98   if (!uri) {
99     fprintf(stderr,
100 	    "ERROR: No device URI supplied!");
101     return (CUPS_BACKEND_FAILED);
102   }
103 
104   ptr = strstr(uri, ":/");
105   if (!ptr) goto bad_uri;
106   ptr += 2;
107   if (*ptr == '0')
108     dd = 0;
109   else if (*ptr == '1')
110     dd = 1;
111   else
112     goto bad_uri;
113   ptr ++;
114   if (*ptr != '/') goto bad_uri;
115   ptr ++;
116   att = 0;
117   while (isdigit(*ptr)) {
118     att = att * 10 + (int)(*ptr) - 48;
119     ptr ++;
120   }
121   if (*ptr != '/') goto bad_uri;
122   ptr ++;
123   delay = 0;
124   while (isdigit(*ptr)) {
125     delay = delay * 10 + (int)(*ptr) - 48;
126     ptr ++;
127   }
128   if (*ptr != '/') goto bad_uri;
129   ptr ++;
130   fprintf(stderr,
131 	  "DEBUG: beh: Don't disable: %d; Attempts: %d; Delay: %d; Destination URI: %s\n",
132 	  dd, att, delay, ptr);
133 
134  /*
135   * If reading from stdin, write everything into a temporary file
136   */
137 
138   if (argc == 6) {
139     char *tmpdir;
140     int fd;
141     FILE *tmpfile;
142     size_t bytes;
143 
144     tmpdir = getenv("TMPDIR");
145     if (!tmpdir)
146       tmpdir = "/tmp";
147     snprintf(tmpfilename, sizeof(tmpfilename), "%s/beh-XXXXXX", tmpdir);
148     fd = mkstemp(tmpfilename);
149     if (fd < 0) {
150       fprintf(stderr,
151 	      "ERROR: beh: Could not create temporary file: %s\n",
152 	      strerror(errno));
153       return (CUPS_BACKEND_FAILED);
154     }
155     tmpfile = fdopen(fd, "r+");
156     while ((bytes = fread(buf, 1, sizeof(buf), stdin)))
157       fwrite(buf, 1, bytes, tmpfile);
158     fclose(tmpfile);
159 
160     filename = tmpfilename;
161   } else {
162     tmpfilename[0] = '\0';
163     filename = argv[6];
164   }
165 
166  /*
167   * Do it!
168   */
169 
170   while ((retval = call_backend(ptr, argc, argv, filename)) !=
171 	 CUPS_BACKEND_OK &&
172 	 !job_canceled) {
173     if (att > 0) {
174       att --;
175       if (att == 0)
176 	break;
177     }
178     if (delay > 0)
179       sleep (delay);
180   }
181 
182   if (strlen(tmpfilename) > 0)
183     unlink(tmpfilename);
184 
185  /*
186   * Return the exit value of the backend only if requested
187   */
188 
189   if (!dd)
190     return (retval);
191   else
192     return (CUPS_BACKEND_OK);
193 
194  /*
195   * Error handling
196   */
197 
198  bad_uri:
199 
200   fprintf(stderr,
201 	  "ERROR: URI must be \"beh:/<dd>/<att>/<delay>/<original uri>\"!\n");
202   return (CUPS_BACKEND_FAILED);
203 }
204 
205 
206 /*
207  * 'call_backend()' - Execute the command line of the destination backend
208  */
209 
210 static int
call_backend(char * uri,int argc,char ** argv,char * filename)211 call_backend(char *uri,                 /* I - URI of final destination */
212 	     int  argc,                 /* I - Number of command line
213 	                                       arguments */
214 	     char **argv,		/* I - Command-line arguments */
215 	     char *filename) {          /* I - File name of input data */
216   const char	*cups_serverbin;	/* Location of programs */
217   char          *backend_argv[8];       // Arguments for called CUPS backend
218   char		scheme[1024],           // Scheme from URI
219                 *ptr,			// Pointer into scheme
220 		backend_path[2048];	// Backend path
221   int           pid,
222                 wait_pid,
223                 wait_status,
224                 retval = 0;
225   int           bytes;
226 
227  /*
228   * Build the backend command line...
229   */
230 
231   scheme[0] = '\0';
232   strncat(scheme, uri, sizeof(scheme) - 1);
233   if ((ptr = strchr(scheme, ':')) != NULL)
234     *ptr = '\0';
235   else
236   {
237     fprintf(stderr,
238 	    "ERROR: beh: Invalid URI, no colon (':') to mark end of scheme part.\n");
239     exit (CUPS_BACKEND_FAILED);
240   }
241   if (strchr(scheme, '/'))
242   {
243     fprintf(stderr,
244 	    "ERROR: beh: Invalid URI, scheme contains a slash ('/').\n");
245     exit (CUPS_BACKEND_FAILED);
246   }
247   if (!strcmp(scheme, ".") || !strcmp(scheme, ".."))
248   {
249     fprintf(stderr,
250 	    "ERROR: beh: Invalid URI, scheme (\"%s\") is a directory.\n",
251 	    scheme);
252     exit (CUPS_BACKEND_FAILED);
253   }
254 
255   if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
256     cups_serverbin = CUPS_SERVERBIN;
257 
258   if (!strncasecmp(uri, "file:", 5) || uri[0] == '/') {
259     fprintf(stderr,
260 	    "ERROR: beh: Direct output into a file not supported.\n");
261     exit (CUPS_BACKEND_FAILED);
262   }
263 
264   backend_argv[0] = uri;
265   backend_argv[1] = argv[1];
266   backend_argv[2] = argv[2];
267   backend_argv[3] = argv[3];
268   backend_argv[4] = (argc == 6 ? "1" : argv[4]);
269   backend_argv[5] = argv[5];
270   backend_argv[6] = filename;
271   backend_argv[7] = NULL;
272 
273   bytes = snprintf(backend_path, sizeof(backend_path),
274 		   "%s/backend/%s", cups_serverbin, scheme);
275   if (bytes < 0 || bytes >= sizeof(backend_path))
276   {
277     fprintf(stderr,
278 	    "ERROR: beh: Invalid scheme (\"%s\"), could not determing backend path.\n",
279 	    scheme);
280     exit (CUPS_BACKEND_FAILED);
281   }
282 
283  /*
284   * Overwrite the device URI and run the actual backend...
285   */
286 
287   setenv("DEVICE_URI", uri, 1);
288 
289   fprintf(stderr,
290 	  "DEBUG: beh: Executing backend command line \"%s '%s' '%s' '%s' '%s' '%s'%s%s\"...\n",
291 	  backend_path, backend_argv[1], backend_argv[2], backend_argv[3],
292 	  backend_argv[4], backend_argv[5],
293 	  (backend_argv[6] && backend_argv[6][0] ? " " : ""),
294 	  (backend_argv[6] && backend_argv[6][0] ? backend_argv[6] : ""));
295   fprintf(stderr,
296 	  "DEBUG: beh: Using device URI: %s\n",
297 	  uri);
298 
299   if ((pid = fork()) == 0)
300   {
301     retval = execv(backend_path, backend_argv);
302     if (retval == -1)
303       fprintf(stderr, "ERROR: Unable to execute backend: %s\n",
304 	      strerror(errno));
305     exit (CUPS_BACKEND_FAILED);
306   }
307   else if (pid < 0)
308   {
309     fprintf(stderr, "ERROR: Unable to fork for backend\n");
310     return (CUPS_BACKEND_FAILED);
311   }
312 
313   while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR);
314 
315   if (wait_pid >= 0 && wait_status)
316   {
317     if (WIFEXITED(wait_status))
318       retval = WEXITSTATUS(wait_status);
319     else if (WTERMSIG(wait_status) != SIGTERM)
320       retval = WTERMSIG(wait_status);
321     else
322       retval = 0;
323   }
324 
325   return (retval);
326 }
327 
328 
329 /*
330  * 'sigterm_handler()' - Handle termination signals.
331  */
332 
333 static void
sigterm_handler(int sig)334 sigterm_handler(int sig) {		/* I - Signal number (unused) */
335   (void)sig;
336 
337   const char * const msg = "DEBUG: beh: Job canceled.\n";
338   // The if() is to eliminate the return value and silence the warning
339   // about an unused return value.
340   if (write(2, msg, strlen(msg)));
341 
342   if (job_canceled)
343     _exit(CUPS_BACKEND_OK);
344   else
345     job_canceled = 1;
346 }
347