• 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 
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